xref: /onnv-gate/usr/src/uts/intel/io/pci/pci_pci.c (revision 7542)
13446Smrj /*
23446Smrj  * CDDL HEADER START
33446Smrj  *
43446Smrj  * The contents of this file are subject to the terms of the
53446Smrj  * Common Development and Distribution License (the "License").
63446Smrj  * You may not use this file except in compliance with the License.
73446Smrj  *
83446Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93446Smrj  * or http://www.opensolaris.org/os/licensing.
103446Smrj  * See the License for the specific language governing permissions
113446Smrj  * and limitations under the License.
123446Smrj  *
133446Smrj  * When distributing Covered Code, include this CDDL HEADER in each
143446Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153446Smrj  * If applicable, add the following below this CDDL HEADER, with the
163446Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
173446Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
183446Smrj  *
193446Smrj  * CDDL HEADER END
203446Smrj  */
213446Smrj /*
226313Skrishnae  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
233446Smrj  * Use is subject to license terms.
243446Smrj  */
253446Smrj 
263446Smrj /*
273446Smrj  * PCI to PCI bus bridge nexus driver
283446Smrj  */
293446Smrj 
303446Smrj #include <sys/conf.h>
313446Smrj #include <sys/kmem.h>
323446Smrj #include <sys/debug.h>
333446Smrj #include <sys/modctl.h>
343446Smrj #include <sys/autoconf.h>
353446Smrj #include <sys/ddi_impldefs.h>
363446Smrj #include <sys/pci.h>
376313Skrishnae #include <sys/pcie_impl.h>
383446Smrj #include <sys/ddi.h>
393446Smrj #include <sys/sunddi.h>
403446Smrj #include <sys/sunndi.h>
413446Smrj #include <sys/ddifm.h>
423446Smrj #include <sys/ndifm.h>
433446Smrj #include <sys/fm/protocol.h>
443446Smrj #include <sys/hotplug/pci/pcihp.h>
453446Smrj #include <sys/pci_intr_lib.h>
463446Smrj #include <sys/psm.h>
473446Smrj 
483446Smrj /*
493446Smrj  * The variable controls the default setting of the command register
503446Smrj  * for pci devices.  See ppb_initchild() for details.
513446Smrj  */
523446Smrj static ushort_t ppb_command_default = PCI_COMM_ME | PCI_COMM_MAE | PCI_COMM_IO;
533446Smrj 
543446Smrj 
553446Smrj static int	ppb_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *,
563446Smrj 		    off_t, off_t, caddr_t *);
573446Smrj static int	ppb_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
583446Smrj 		    void *, void *);
593446Smrj static int	ppb_fm_init(dev_info_t *, dev_info_t *, int,
603446Smrj 		    ddi_iblock_cookie_t *);
613446Smrj static int	ppb_fm_callback(dev_info_t *, ddi_fm_error_t *, const void *);
623446Smrj static int	ppb_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t,
633446Smrj 		    ddi_intr_handle_impl_t *, void *);
643446Smrj 
653446Smrj /*
663446Smrj  * ppb_support_msi: Flag that controls MSI support across P2P Bridges.
673446Smrj  * By default, MSI is not supported except for special cases like HT
683446Smrj  * bridges/tunnels that have HT MSI mapping enabled.
693446Smrj  *
703446Smrj  * However, MSI support behavior can be patched on a system by changing
713446Smrj  * the value of this flag as shown below:-
723446Smrj  *	 0 = default value, MSI is allowed by this driver for special cases
733446Smrj  *	 1 = MSI supported without any checks for this driver
743446Smrj  *	-1 = MSI not supported at all
753446Smrj  */
763446Smrj int ppb_support_msi = 0;
773446Smrj 
783446Smrj /*
793446Smrj  * Controls the usage of the Hypertransport MSI mapping capability
803446Smrj  *	0 = default value, leave hardware function as it is
813446Smrj  *	1 = always enable HT MSI mapping
823446Smrj  *     -1 = always disable HT MSI mapping
833446Smrj  */
843446Smrj int ppb_support_ht_msimap = 0;
853446Smrj 
863446Smrj /*
873446Smrj  * masks and values for the upper 16-bits of hypertransport cap headers
883446Smrj  */
893446Smrj #define	PCI_CAP_HT_MSIMAP_TYPE			0xA800
903446Smrj #define	PCI_CAP_HT_MSIMAP_TYPE_MASK		0xFF00
913446Smrj #define	PCI_CAP_HT_MSIMAP_ENABLE		0x0001
923446Smrj #define	PCI_CAP_HT_MSIMAP_ENABLE_MASK		0x0001
933446Smrj 
943446Smrj 
953446Smrj struct bus_ops ppb_bus_ops = {
963446Smrj 	BUSO_REV,
973446Smrj 	ppb_bus_map,
983446Smrj 	0,
993446Smrj 	0,
1003446Smrj 	0,
1013446Smrj 	i_ddi_map_fault,
1023446Smrj 	ddi_dma_map,
1033446Smrj 	ddi_dma_allochdl,
1043446Smrj 	ddi_dma_freehdl,
1053446Smrj 	ddi_dma_bindhdl,
1063446Smrj 	ddi_dma_unbindhdl,
1073446Smrj 	ddi_dma_flush,
1083446Smrj 	ddi_dma_win,
1093446Smrj 	ddi_dma_mctl,
1103446Smrj 	ppb_ctlops,
1113446Smrj 	ddi_bus_prop_op,
1123446Smrj 	0,		/* (*bus_get_eventcookie)();	*/
1133446Smrj 	0,		/* (*bus_add_eventcall)();	*/
1143446Smrj 	0,		/* (*bus_remove_eventcall)();	*/
1153446Smrj 	0,		/* (*bus_post_event)();		*/
1163446Smrj 	0,		/* (*bus_intr_ctl)();		*/
1173446Smrj 	0,		/* (*bus_config)(); 		*/
1183446Smrj 	0,		/* (*bus_unconfig)(); 		*/
1193446Smrj 	ppb_fm_init,	/* (*bus_fm_init)(); 		*/
1203446Smrj 	NULL,		/* (*bus_fm_fini)(); 		*/
1213446Smrj 	NULL,		/* (*bus_fm_access_enter)(); 	*/
1223446Smrj 	NULL,		/* (*bus_fm_access_exit)(); 	*/
1233446Smrj 	NULL,		/* (*bus_power)(); 	*/
1243446Smrj 	ppb_intr_ops	/* (*bus_intr_op)(); 		*/
1253446Smrj };
1263446Smrj 
1273446Smrj /*
1283446Smrj  * The goal here is to leverage off of the pcihp.c source without making
1293446Smrj  * changes to it.  Call into it's cb_ops directly if needed.
1303446Smrj  */
1313446Smrj static int	ppb_open(dev_t *, int, int, cred_t *);
1323446Smrj static int	ppb_close(dev_t, int, int, cred_t *);
1333446Smrj static int	ppb_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1343446Smrj static int	ppb_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
1353446Smrj 		    caddr_t, int *);
1363446Smrj static int	ppb_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
1376313Skrishnae static void	ppb_peekpoke_cb(dev_info_t *, ddi_fm_error_t *);
1383446Smrj 
1393446Smrj struct cb_ops ppb_cb_ops = {
1403446Smrj 	ppb_open,			/* open */
1413446Smrj 	ppb_close,			/* close */
1423446Smrj 	nodev,				/* strategy */
1433446Smrj 	nodev,				/* print */
1443446Smrj 	nodev,				/* dump */
1453446Smrj 	nodev,				/* read */
1463446Smrj 	nodev,				/* write */
1473446Smrj 	ppb_ioctl,			/* ioctl */
1483446Smrj 	nodev,				/* devmap */
1493446Smrj 	nodev,				/* mmap */
1503446Smrj 	nodev,				/* segmap */
1513446Smrj 	nochpoll,			/* poll */
1523446Smrj 	ppb_prop_op,			/* cb_prop_op */
1533446Smrj 	NULL,				/* streamtab */
1543446Smrj 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
1553446Smrj 	CB_REV,				/* rev */
1563446Smrj 	nodev,				/* int (*cb_aread)() */
1573446Smrj 	nodev				/* int (*cb_awrite)() */
1583446Smrj };
1593446Smrj 
1603446Smrj 
1613446Smrj static int ppb_probe(dev_info_t *);
1623446Smrj static int ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
1633446Smrj static int ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
1643446Smrj 
1653446Smrj struct dev_ops ppb_ops = {
1663446Smrj 	DEVO_REV,		/* devo_rev */
1673446Smrj 	0,			/* refcnt  */
1683446Smrj 	ppb_info,		/* info */
1693446Smrj 	nulldev,		/* identify */
1703446Smrj 	ppb_probe,		/* probe */
1713446Smrj 	ppb_attach,		/* attach */
1723446Smrj 	ppb_detach,		/* detach */
1733446Smrj 	nulldev,		/* reset */
1743446Smrj 	&ppb_cb_ops,		/* driver operations */
1753446Smrj 	&ppb_bus_ops		/* bus operations */
1763446Smrj };
1773446Smrj 
1783446Smrj /*
1793446Smrj  * Module linkage information for the kernel.
1803446Smrj  */
1813446Smrj 
1823446Smrj static struct modldrv modldrv = {
1833446Smrj 	&mod_driverops, /* Type of module */
184*7542SRichard.Bean@Sun.COM 	"PCI to PCI bridge nexus driver",
1853446Smrj 	&ppb_ops,	/* driver ops */
1863446Smrj };
1873446Smrj 
1883446Smrj static struct modlinkage modlinkage = {
1893446Smrj 	MODREV_1,
1903446Smrj 	(void *)&modldrv,
1913446Smrj 	NULL
1923446Smrj };
1933446Smrj 
1943446Smrj /*
1953446Smrj  * soft state pointer and structure template:
1963446Smrj  */
1973446Smrj static void *ppb_state;
1983446Smrj 
1993446Smrj typedef struct {
2003446Smrj 	dev_info_t *dip;
2013446Smrj 	int ppb_fmcap;
2023446Smrj 	ddi_iblock_cookie_t ppb_fm_ibc;
2033446Smrj 	kmutex_t ppb_peek_poke_mutex;
2043446Smrj 	kmutex_t ppb_err_mutex;
2053446Smrj 
2063446Smrj 	/*
2073446Smrj 	 * cpr support:
2083446Smrj 	 */
2093446Smrj 	uint_t config_state_index;
2103446Smrj 	struct {
2113446Smrj 		dev_info_t *dip;
2123446Smrj 		ushort_t command;
2133446Smrj 		uchar_t cache_line_size;
2143446Smrj 		uchar_t latency_timer;
2153446Smrj 		uchar_t header_type;
2163446Smrj 		uchar_t sec_latency_timer;
2173446Smrj 		ushort_t bridge_control;
2183446Smrj 	} config_state[PCI_MAX_CHILDREN];
2196313Skrishnae 
2206313Skrishnae 	uint8_t parent_bus;
2213446Smrj } ppb_devstate_t;
2223446Smrj 
2233446Smrj 
2243446Smrj /*
2253446Smrj  * forward function declarations:
2263446Smrj  */
2273446Smrj static void	ppb_removechild(dev_info_t *);
2283446Smrj static int	ppb_initchild(dev_info_t *child);
2293446Smrj static void	ppb_save_config_regs(ppb_devstate_t *ppb_p);
2303446Smrj static void	ppb_restore_config_regs(ppb_devstate_t *ppb_p);
2313446Smrj static uint8_t	ppb_find_ht_cap(ddi_acc_handle_t cfg_hdl, uint16_t reg_mask,
2323446Smrj 		    uint16_t reg_val);
2333446Smrj static boolean_t	ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl);
2343446Smrj static int	ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd);
2353446Smrj 
2363446Smrj /*
2373446Smrj  * for <cmd> in ppb_ht_msimap_set
2383446Smrj  */
2393446Smrj #define	HT_MSIMAP_ENABLE	1
2403446Smrj #define	HT_MSIMAP_DISABLE	0
2413446Smrj 
2423446Smrj 
2433446Smrj int
2443446Smrj _init(void)
2453446Smrj {
2463446Smrj 	int e;
2473446Smrj 
2483446Smrj 	if ((e = ddi_soft_state_init(&ppb_state, sizeof (ppb_devstate_t),
2493446Smrj 	    1)) == 0 && (e = mod_install(&modlinkage)) != 0)
2503446Smrj 		ddi_soft_state_fini(&ppb_state);
2513446Smrj 	return (e);
2523446Smrj }
2533446Smrj 
2543446Smrj int
2553446Smrj _fini(void)
2563446Smrj {
2573446Smrj 	int e;
2583446Smrj 
2593446Smrj 	if ((e = mod_remove(&modlinkage)) == 0)
2603446Smrj 		ddi_soft_state_fini(&ppb_state);
2613446Smrj 	return (e);
2623446Smrj }
2633446Smrj 
2643446Smrj int
2653446Smrj _info(struct modinfo *modinfop)
2663446Smrj {
2673446Smrj 	return (mod_info(&modlinkage, modinfop));
2683446Smrj }
2693446Smrj 
2703446Smrj /*ARGSUSED*/
2713446Smrj static int
2723446Smrj ppb_probe(dev_info_t *devi)
2733446Smrj {
2743446Smrj 	return (DDI_PROBE_SUCCESS);
2753446Smrj }
2763446Smrj 
2773446Smrj /*ARGSUSED*/
2783446Smrj static int
2793446Smrj ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
2803446Smrj {
2816313Skrishnae 	dev_info_t *root = ddi_root_node();
2823446Smrj 	int instance;
2833446Smrj 	ppb_devstate_t *ppb;
2846313Skrishnae 	dev_info_t *pdip;
2853446Smrj 	ddi_acc_handle_t config_handle;
2866313Skrishnae 	char *bus;
2873446Smrj 
2883446Smrj 	switch (cmd) {
2893446Smrj 	case DDI_ATTACH:
2903446Smrj 
2913446Smrj 		/*
2923446Smrj 		 * Make sure the "device_type" property exists.
2933446Smrj 		 */
2943446Smrj 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
2953446Smrj 		    "device_type", "pci");
2963446Smrj 
2973446Smrj 		/*
2983446Smrj 		 * Allocate and get soft state structure.
2993446Smrj 		 */
3003446Smrj 		instance = ddi_get_instance(devi);
3013446Smrj 		if (ddi_soft_state_zalloc(ppb_state, instance) != DDI_SUCCESS)
3023446Smrj 			return (DDI_FAILURE);
3033446Smrj 		ppb = ddi_get_soft_state(ppb_state, instance);
3043446Smrj 		ppb->dip = devi;
3053446Smrj 
3063446Smrj 		/*
3073446Smrj 		 * don't enable ereports if immediate child of npe
3083446Smrj 		 */
3093446Smrj 		if (strcmp(ddi_driver_name(ddi_get_parent(devi)), "npe") == 0)
3103446Smrj 			ppb->ppb_fmcap = DDI_FM_ERRCB_CAPABLE |
3113446Smrj 			    DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
3123446Smrj 		else
3133446Smrj 			ppb->ppb_fmcap = DDI_FM_EREPORT_CAPABLE |
3143446Smrj 			    DDI_FM_ERRCB_CAPABLE | DDI_FM_ACCCHK_CAPABLE |
3153446Smrj 			    DDI_FM_DMACHK_CAPABLE;
3163446Smrj 
3173446Smrj 		ddi_fm_init(devi, &ppb->ppb_fmcap, &ppb->ppb_fm_ibc);
3183446Smrj 		mutex_init(&ppb->ppb_err_mutex, NULL, MUTEX_DRIVER,
3193446Smrj 		    (void *)ppb->ppb_fm_ibc);
3203446Smrj 		mutex_init(&ppb->ppb_peek_poke_mutex, NULL, MUTEX_DRIVER,
3213446Smrj 		    (void *)ppb->ppb_fm_ibc);
3223446Smrj 
3233446Smrj 		if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
3243446Smrj 		    DDI_FM_EREPORT_CAPABLE))
3253446Smrj 			pci_ereport_setup(devi);
3263446Smrj 		if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
3275295Srandyf 			ddi_fm_handler_register(devi, ppb_fm_callback, NULL);
3283446Smrj 
3293446Smrj 		if (pci_config_setup(devi, &config_handle) != DDI_SUCCESS) {
3303446Smrj 			if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
3313446Smrj 				ddi_fm_handler_unregister(devi);
3323446Smrj 			if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
3333446Smrj 			    DDI_FM_EREPORT_CAPABLE))
3343446Smrj 				pci_ereport_teardown(devi);
3353446Smrj 			ddi_fm_fini(devi);
3363446Smrj 			ddi_soft_state_free(ppb_state, instance);
3373446Smrj 			return (DDI_FAILURE);
3383446Smrj 		}
3393446Smrj 
3406313Skrishnae 		ppb->parent_bus = PCIE_PCIECAP_DEV_TYPE_PCI_DEV;
3416313Skrishnae 		for (pdip = ddi_get_parent(devi); pdip && (pdip != root) &&
3426313Skrishnae 		    (ppb->parent_bus != PCIE_PCIECAP_DEV_TYPE_PCIE_DEV);
3436313Skrishnae 		    pdip = ddi_get_parent(pdip)) {
3446313Skrishnae 			if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip,
3456313Skrishnae 			    DDI_PROP_DONTPASS, "device_type", &bus) !=
3466313Skrishnae 			    DDI_PROP_SUCCESS)
3476313Skrishnae 				break;
3486313Skrishnae 
3496313Skrishnae 			if (strcmp(bus, "pciex") == 0)
3506313Skrishnae 				ppb->parent_bus =
3516313Skrishnae 				    PCIE_PCIECAP_DEV_TYPE_PCIE_DEV;
3526313Skrishnae 
3536313Skrishnae 			ddi_prop_free(bus);
3546313Skrishnae 		}
3556313Skrishnae 
3563446Smrj 		if (ppb_support_ht_msimap == 1)
3573446Smrj 			(void) ppb_ht_msimap_set(config_handle,
3583446Smrj 			    HT_MSIMAP_ENABLE);
3593446Smrj 		else if (ppb_support_ht_msimap == -1)
3603446Smrj 			(void) ppb_ht_msimap_set(config_handle,
3613446Smrj 			    HT_MSIMAP_DISABLE);
3623446Smrj 
3633446Smrj 		pci_config_teardown(&config_handle);
3643446Smrj 
3653446Smrj 		/*
3663446Smrj 		 * Initialize hotplug support on this bus. At minimum
3673446Smrj 		 * (for non hotplug bus) this would create ":devctl" minor
3683446Smrj 		 * node to support DEVCTL_DEVICE_* and DEVCTL_BUS_* ioctls
3693446Smrj 		 * to this bus.
3703446Smrj 		 */
3713446Smrj 		if (pcihp_init(devi) != DDI_SUCCESS)
3725295Srandyf 			cmn_err(CE_WARN,
3735295Srandyf 			    "pci: Failed to setup hotplug framework");
3743446Smrj 
3753446Smrj 		ddi_report_dev(devi);
3763446Smrj 		return (DDI_SUCCESS);
3773446Smrj 
3783446Smrj 	case DDI_RESUME:
3793446Smrj 
3803446Smrj 		/*
3813446Smrj 		 * Get the soft state structure for the bridge.
3823446Smrj 		 */
3833446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
3843446Smrj 		ppb_restore_config_regs(ppb);
3853446Smrj 		return (DDI_SUCCESS);
3863446Smrj 
3873446Smrj 	default:
3883446Smrj 		break;
3893446Smrj 	}
3903446Smrj 	return (DDI_FAILURE);
3913446Smrj }
3923446Smrj 
3933446Smrj /*ARGSUSED*/
3943446Smrj static int
3953446Smrj ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
3963446Smrj {
3973446Smrj 	ppb_devstate_t *ppb;
3983446Smrj 
3993446Smrj 	switch (cmd) {
4003446Smrj 	case DDI_DETACH:
4013446Smrj 		(void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type");
4023446Smrj 
4033446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
4043446Smrj 		if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
4053446Smrj 			ddi_fm_handler_unregister(devi);
4063446Smrj 		if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
4073446Smrj 		    DDI_FM_EREPORT_CAPABLE))
4083446Smrj 			pci_ereport_teardown(devi);
4093446Smrj 		mutex_destroy(&ppb->ppb_peek_poke_mutex);
4103446Smrj 		mutex_destroy(&ppb->ppb_err_mutex);
4113446Smrj 		ddi_fm_fini(devi);
4123446Smrj 
4133446Smrj 		/*
4143446Smrj 		 * And finally free the per-pci soft state.
4153446Smrj 		 */
4163446Smrj 		ddi_soft_state_free(ppb_state, ddi_get_instance(devi));
4173446Smrj 
4183446Smrj 		/*
4193446Smrj 		 * Uninitialize hotplug support on this bus.
4203446Smrj 		 */
4213446Smrj 		(void) pcihp_uninit(devi);
4223446Smrj 		return (DDI_SUCCESS);
4233446Smrj 
4243446Smrj 	case DDI_SUSPEND:
4253446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
4263446Smrj 		ppb_save_config_regs(ppb);
4273446Smrj 		return (DDI_SUCCESS);
4283446Smrj 
4293446Smrj 	default:
4303446Smrj 		break;
4313446Smrj 	}
4323446Smrj 	return (DDI_FAILURE);
4333446Smrj }
4343446Smrj 
4353446Smrj /*ARGSUSED*/
4363446Smrj static int
4373446Smrj ppb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
4383446Smrj 	off_t offset, off_t len, caddr_t *vaddrp)
4393446Smrj {
4403446Smrj 	dev_info_t *pdip;
4413446Smrj 
4423446Smrj 	pdip = (dev_info_t *)DEVI(dip)->devi_parent;
4433446Smrj 	return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip,
4445295Srandyf 	    rdip, mp, offset, len, vaddrp));
4453446Smrj }
4463446Smrj 
4473446Smrj /*ARGSUSED*/
4483446Smrj static int
4493446Smrj ppb_ctlops(dev_info_t *dip, dev_info_t *rdip,
4503446Smrj 	ddi_ctl_enum_t ctlop, void *arg, void *result)
4513446Smrj {
4523446Smrj 	pci_regspec_t *drv_regp;
4533446Smrj 	int	reglen;
4543446Smrj 	int	rn;
4553446Smrj 	int	totreg;
4566313Skrishnae 	ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state,
4576313Skrishnae 	    ddi_get_instance(dip));
4586313Skrishnae 	struct detachspec *dsp;
4595295Srandyf 	struct attachspec *asp;
4603446Smrj 
4613446Smrj 	switch (ctlop) {
4623446Smrj 	case DDI_CTLOPS_REPORTDEV:
4633446Smrj 		if (rdip == (dev_info_t *)0)
4643446Smrj 			return (DDI_FAILURE);
4653446Smrj 		cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n",
4663446Smrj 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
4673446Smrj 		    ddi_driver_name(rdip),
4683446Smrj 		    ddi_get_instance(rdip));
4693446Smrj 		return (DDI_SUCCESS);
4703446Smrj 
4713446Smrj 	case DDI_CTLOPS_INITCHILD:
4723446Smrj 		return (ppb_initchild((dev_info_t *)arg));
4733446Smrj 
4743446Smrj 	case DDI_CTLOPS_UNINITCHILD:
4753446Smrj 		ppb_removechild((dev_info_t *)arg);
4763446Smrj 		return (DDI_SUCCESS);
4773446Smrj 
4783446Smrj 	case DDI_CTLOPS_SIDDEV:
4793446Smrj 		return (DDI_SUCCESS);
4803446Smrj 
4813446Smrj 	case DDI_CTLOPS_REGSIZE:
4823446Smrj 	case DDI_CTLOPS_NREGS:
4833446Smrj 		if (rdip == (dev_info_t *)0)
4843446Smrj 			return (DDI_FAILURE);
4853446Smrj 		break;
4863446Smrj 
4875295Srandyf 	/* X86 systems support PME wakeup from suspend */
4885295Srandyf 	case DDI_CTLOPS_ATTACH:
4896313Skrishnae 		if (!pcie_is_child(dip, rdip))
4906313Skrishnae 			return (DDI_SUCCESS);
4916313Skrishnae 
4925295Srandyf 		asp = (struct attachspec *)arg;
4936313Skrishnae 		if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) &&
4946313Skrishnae 		    (asp->when == DDI_POST) && (asp->result == DDI_SUCCESS))
4956313Skrishnae 			pf_init(rdip, (void *)ppb->ppb_fm_ibc, asp->cmd);
4966313Skrishnae 
4975295Srandyf 		if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE)
4985295Srandyf 			if (pci_pre_resume(rdip) != DDI_SUCCESS)
4995295Srandyf 				return (DDI_FAILURE);
5005295Srandyf 
5016313Skrishnae 		return (DDI_SUCCESS);
5025295Srandyf 
5035295Srandyf 	case DDI_CTLOPS_DETACH:
5046313Skrishnae 		if (!pcie_is_child(dip, rdip))
5056313Skrishnae 			return (DDI_SUCCESS);
5066313Skrishnae 
5076313Skrishnae 		dsp = (struct detachspec *)arg;
5086313Skrishnae 		if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) &&
5096313Skrishnae 		    (dsp->when == DDI_PRE))
5106313Skrishnae 			pf_fini(rdip, dsp->cmd);
5116313Skrishnae 
5126313Skrishnae 		if (dsp->cmd == DDI_SUSPEND && dsp->when == DDI_POST)
5135295Srandyf 			if (pci_post_suspend(rdip) != DDI_SUCCESS)
5145295Srandyf 				return (DDI_FAILURE);
5156313Skrishnae 
5166313Skrishnae 		return (DDI_SUCCESS);
5175295Srandyf 
5183446Smrj 	case DDI_CTLOPS_PEEK:
5193446Smrj 	case DDI_CTLOPS_POKE:
5203446Smrj 		if (strcmp(ddi_driver_name(ddi_get_parent(dip)), "npe") != 0)
5213446Smrj 			return (ddi_ctlops(dip, rdip, ctlop, arg, result));
5223446Smrj 		return (pci_peekpoke_check(dip, rdip, ctlop, arg, result,
5233446Smrj 		    ddi_ctlops, &ppb->ppb_err_mutex,
5246313Skrishnae 		    &ppb->ppb_peek_poke_mutex, ppb_peekpoke_cb));
5253446Smrj 
5263446Smrj 	default:
5273446Smrj 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
5283446Smrj 	}
5293446Smrj 
5303446Smrj 	*(int *)result = 0;
5313446Smrj 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip,
5325295Srandyf 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg",
5335295Srandyf 	    (caddr_t)&drv_regp, &reglen) != DDI_SUCCESS)
5343446Smrj 		return (DDI_FAILURE);
5353446Smrj 
5363446Smrj 	totreg = reglen / sizeof (pci_regspec_t);
5373446Smrj 	if (ctlop == DDI_CTLOPS_NREGS)
5383446Smrj 		*(int *)result = totreg;
5393446Smrj 	else if (ctlop == DDI_CTLOPS_REGSIZE) {
5403446Smrj 		rn = *(int *)arg;
5413446Smrj 		if (rn >= totreg) {
5423446Smrj 			kmem_free(drv_regp, reglen);
5433446Smrj 			return (DDI_FAILURE);
5443446Smrj 		}
5453446Smrj 		*(off_t *)result = drv_regp[rn].pci_size_low;
5463446Smrj 	}
5473446Smrj 
5483446Smrj 	kmem_free(drv_regp, reglen);
5493446Smrj 	return (DDI_SUCCESS);
5503446Smrj }
5513446Smrj 
5523446Smrj static int
5533446Smrj ppb_name_child(dev_info_t *child, char *name, int namelen)
5543446Smrj {
5553446Smrj 	pci_regspec_t *pci_rp;
5563446Smrj 	uint_t slot, func;
5573446Smrj 	char **unit_addr;
5583446Smrj 	uint_t n;
5593446Smrj 
5603446Smrj 	/*
5613446Smrj 	 * For .conf nodes, use unit-address property as name
5623446Smrj 	 */
5633446Smrj 	if (ndi_dev_is_persistent_node(child) == 0) {
5643446Smrj 		if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
5653446Smrj 		    DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) !=
5663446Smrj 		    DDI_PROP_SUCCESS) {
5673446Smrj 			cmn_err(CE_WARN,
5683446Smrj 			    "cannot find unit-address in %s.conf",
5693446Smrj 			    ddi_driver_name(child));
5703446Smrj 			return (DDI_FAILURE);
5713446Smrj 		}
5723446Smrj 		if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
5733446Smrj 			cmn_err(CE_WARN, "unit-address property in %s.conf"
5743446Smrj 			    " not well-formed", ddi_driver_name(child));
5753446Smrj 			ddi_prop_free(unit_addr);
5763446Smrj 			return (DDI_SUCCESS);
5773446Smrj 		}
5783446Smrj 		(void) snprintf(name, namelen, "%s", *unit_addr);
5793446Smrj 		ddi_prop_free(unit_addr);
5803446Smrj 		return (DDI_SUCCESS);
5813446Smrj 	}
5823446Smrj 
5833446Smrj 	/* get child "reg" property */
5843446Smrj 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
5853446Smrj 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) {
5863446Smrj 		return (DDI_FAILURE);
5873446Smrj 	}
5883446Smrj 
5893446Smrj 	/* copy the device identifications */
5903446Smrj 	slot = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
5913446Smrj 	func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
5923446Smrj 
5933446Smrj 	if (func != 0)
5943446Smrj 		(void) snprintf(name, namelen, "%x,%x", slot, func);
5953446Smrj 	else
5963446Smrj 		(void) snprintf(name, namelen, "%x", slot);
5973446Smrj 
5983446Smrj 	ddi_prop_free(pci_rp);
5993446Smrj 	return (DDI_SUCCESS);
6003446Smrj }
6013446Smrj 
6023446Smrj static int
6033446Smrj ppb_initchild(dev_info_t *child)
6043446Smrj {
6053446Smrj 	struct ddi_parent_private_data *pdptr;
6066313Skrishnae 	ppb_devstate_t *ppb;
6073446Smrj 	char name[MAXNAMELEN];
6083446Smrj 	ddi_acc_handle_t config_handle;
6093446Smrj 	ushort_t command_preserve, command;
6103446Smrj 
6116313Skrishnae 	ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
6126313Skrishnae 	    ddi_get_instance(ddi_get_parent(child)));
6136313Skrishnae 
6143446Smrj 	if (ppb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS)
6153446Smrj 		return (DDI_FAILURE);
6163446Smrj 	ddi_set_name_addr(child, name);
6173446Smrj 
6183446Smrj 	/*
6193446Smrj 	 * Pseudo nodes indicate a prototype node with per-instance
6203446Smrj 	 * properties to be merged into the real h/w device node.
6213446Smrj 	 * The interpretation of the unit-address is DD[,F]
6223446Smrj 	 * where DD is the device id and F is the function.
6233446Smrj 	 */
6243446Smrj 	if (ndi_dev_is_persistent_node(child) == 0) {
6253446Smrj 		extern int pci_allow_pseudo_children;
6263446Smrj 
6273446Smrj 		ddi_set_parent_data(child, NULL);
6283446Smrj 
6293446Smrj 		/*
6303446Smrj 		 * Try to merge the properties from this prototype
6313446Smrj 		 * node into real h/w nodes.
6323446Smrj 		 */
6333446Smrj 		if (ndi_merge_node(child, ppb_name_child) == DDI_SUCCESS) {
6343446Smrj 			/*
6353446Smrj 			 * Merged ok - return failure to remove the node.
6363446Smrj 			 */
6373446Smrj 			ddi_set_name_addr(child, NULL);
6383446Smrj 			return (DDI_FAILURE);
6393446Smrj 		}
6403446Smrj 
6413446Smrj 		/* workaround for ddivs to run under PCI */
6423446Smrj 		if (pci_allow_pseudo_children)
6433446Smrj 			return (DDI_SUCCESS);
6443446Smrj 
6453446Smrj 		/*
6463446Smrj 		 * The child was not merged into a h/w node,
6473446Smrj 		 * but there's not much we can do with it other
6483446Smrj 		 * than return failure to cause the node to be removed.
6493446Smrj 		 */
6503446Smrj 		cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
6513446Smrj 		    ddi_driver_name(child), ddi_get_name_addr(child),
6523446Smrj 		    ddi_driver_name(child));
6533446Smrj 		ddi_set_name_addr(child, NULL);
6543446Smrj 		return (DDI_NOT_WELL_FORMED);
6553446Smrj 	}
6563446Smrj 
6576313Skrishnae 	ddi_set_parent_data(child, NULL);
6586313Skrishnae 
6596313Skrishnae 	/*
6606313Skrishnae 	 * PCIe FMA specific
6616313Skrishnae 	 *
6626313Skrishnae 	 * Note: parent_data for parent is created only if this is PCI-E
6636313Skrishnae 	 * platform, for which, SG take a different route to handle device
6646313Skrishnae 	 * errors.
6656313Skrishnae 	 */
6666313Skrishnae 	if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
6676313Skrishnae 		if (pcie_init_bus(child) == NULL)
6686313Skrishnae 			return (DDI_FAILURE);
6696313Skrishnae 	}
6706313Skrishnae 
6713446Smrj 	/* transfer select properties from PROM to kernel */
6725295Srandyf 	if (ddi_getprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS,
6735295Srandyf 	    "interrupts", -1) != -1) {
6743446Smrj 		pdptr = kmem_zalloc((sizeof (struct ddi_parent_private_data) +
6753446Smrj 		    sizeof (struct intrspec)), KM_SLEEP);
6763446Smrj 		pdptr->par_intr = (struct intrspec *)(pdptr + 1);
6773446Smrj 		pdptr->par_nintr = 1;
6783446Smrj 		ddi_set_parent_data(child, pdptr);
6793446Smrj 	} else
6803446Smrj 		ddi_set_parent_data(child, NULL);
6813446Smrj 
6823446Smrj 	if (pci_config_setup(child, &config_handle) != DDI_SUCCESS)
6833446Smrj 		return (DDI_FAILURE);
6843446Smrj 
6853446Smrj 	/*
6863446Smrj 	 * Support for the "command-preserve" property.
6873446Smrj 	 */
6883446Smrj 	command_preserve = ddi_prop_get_int(DDI_DEV_T_ANY, child,
6893446Smrj 	    DDI_PROP_DONTPASS, "command-preserve", 0);
6903446Smrj 	command = pci_config_get16(config_handle, PCI_CONF_COMM);
6913446Smrj 	command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB);
6923446Smrj 	command |= (ppb_command_default & ~command_preserve);
6933446Smrj 	pci_config_put16(config_handle, PCI_CONF_COMM, command);
6943446Smrj 
6953446Smrj 	pci_config_teardown(&config_handle);
6963446Smrj 	return (DDI_SUCCESS);
6973446Smrj }
6983446Smrj 
6993446Smrj static void
7003446Smrj ppb_removechild(dev_info_t *dip)
7013446Smrj {
7023446Smrj 	struct ddi_parent_private_data *pdptr;
7036313Skrishnae 	ppb_devstate_t *ppb;
7043446Smrj 
7056313Skrishnae 	ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
7066313Skrishnae 	    ddi_get_instance(ddi_get_parent(dip)));
7076313Skrishnae 
7086313Skrishnae 	if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
7096313Skrishnae 		pcie_fini_bus(dip);
7106313Skrishnae 	else if ((pdptr = ddi_get_parent_data(dip)) != NULL) {
7113446Smrj 		kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec)));
7123446Smrj 		ddi_set_parent_data(dip, NULL);
7133446Smrj 	}
7143446Smrj 	ddi_set_name_addr(dip, NULL);
7153446Smrj 
7163446Smrj 	/*
7173446Smrj 	 * Strip the node to properly convert it back to prototype form
7183446Smrj 	 */
7193446Smrj 	ddi_remove_minor_node(dip, NULL);
7203446Smrj 
7213446Smrj 	impl_rem_dev_props(dip);
7223446Smrj }
7233446Smrj 
7243446Smrj /*
7253446Smrj  * ppb_save_config_regs
7263446Smrj  *
7273446Smrj  * This routine saves the state of the configuration registers of all
7283446Smrj  * the child nodes of each PBM.
7293446Smrj  *
7303446Smrj  * used by: ppb_detach() on suspends
7313446Smrj  *
7323446Smrj  * return value: none
7333446Smrj  */
7343446Smrj static void
7353446Smrj ppb_save_config_regs(ppb_devstate_t *ppb_p)
7363446Smrj {
7373446Smrj 	int i;
7383446Smrj 	dev_info_t *dip;
7393446Smrj 	ddi_acc_handle_t config_handle;
7403446Smrj 
7413446Smrj 	for (i = 0, dip = ddi_get_child(ppb_p->dip); dip != NULL;
7425295Srandyf 	    i++, dip = ddi_get_next_sibling(dip)) {
7433446Smrj 
7443446Smrj 		if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
7453446Smrj 			cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
7465295Srandyf 			    ddi_driver_name(ppb_p->dip),
7475295Srandyf 			    ddi_get_instance(ppb_p->dip),
7485295Srandyf 			    ddi_driver_name(dip),
7495295Srandyf 			    ddi_get_instance(dip));
7503446Smrj 			continue;
7513446Smrj 		}
7523446Smrj 
7533446Smrj 		ppb_p->config_state[i].dip = dip;
7543446Smrj 		ppb_p->config_state[i].command =
7555295Srandyf 		    pci_config_get16(config_handle, PCI_CONF_COMM);
7563446Smrj 		pci_config_teardown(&config_handle);
7573446Smrj 	}
7583446Smrj 	ppb_p->config_state_index = i;
7593446Smrj }
7603446Smrj 
7613446Smrj 
7623446Smrj /*
7633446Smrj  * ppb_restore_config_regs
7643446Smrj  *
7653446Smrj  * This routine restores the state of the configuration registers of all
7663446Smrj  * the child nodes of each PBM.
7673446Smrj  *
7683446Smrj  * used by: ppb_attach() on resume
7693446Smrj  *
7703446Smrj  * return value: none
7713446Smrj  */
7723446Smrj static void
7733446Smrj ppb_restore_config_regs(ppb_devstate_t *ppb_p)
7743446Smrj {
7753446Smrj 	int i;
7763446Smrj 	dev_info_t *dip;
7773446Smrj 	ddi_acc_handle_t config_handle;
7783446Smrj 
7793446Smrj 	for (i = 0; i < ppb_p->config_state_index; i++) {
7803446Smrj 		dip = ppb_p->config_state[i].dip;
7813446Smrj 		if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
7823446Smrj 			cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
7835295Srandyf 			    ddi_driver_name(ppb_p->dip),
7845295Srandyf 			    ddi_get_instance(ppb_p->dip),
7855295Srandyf 			    ddi_driver_name(dip),
7865295Srandyf 			    ddi_get_instance(dip));
7873446Smrj 			continue;
7883446Smrj 		}
7893446Smrj 		pci_config_put16(config_handle, PCI_CONF_COMM,
7905295Srandyf 		    ppb_p->config_state[i].command);
7913446Smrj 		pci_config_teardown(&config_handle);
7923446Smrj 	}
7933446Smrj }
7943446Smrj 
7953446Smrj 
7963446Smrj /*
7973446Smrj  * returns the location of a hypertransport capability whose upper 16-bit
7983446Smrj  * register of the cap header matches <reg_val> after masking the register
7993446Smrj  * with <reg_mask>; if both <reg_mask> and <reg_val> are 0, it will return the
8003446Smrj  * first HT cap found
8013446Smrj  */
8023446Smrj static uint8_t
8033446Smrj ppb_find_ht_cap(ddi_acc_handle_t cfg_hdl, uint16_t reg_mask, uint16_t reg_val)
8043446Smrj {
8053446Smrj 	uint16_t status, reg;
8063446Smrj 	uint8_t ptr, id;
8073446Smrj 
8083446Smrj 	status = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
8093446Smrj 	if (status == 0xffff || !((status & PCI_STAT_CAP)))
8103446Smrj 		return (PCI_CAP_NEXT_PTR_NULL);
8113446Smrj 
8123446Smrj 	ptr = pci_config_get8(cfg_hdl, PCI_CONF_CAP_PTR);
8133446Smrj 	while (ptr != 0xFF &&
8143446Smrj 	    ptr != PCI_CAP_NEXT_PTR_NULL &&
8153446Smrj 	    ptr >= PCI_CAP_PTR_OFF) {
8163446Smrj 
8173446Smrj 		ptr &= PCI_CAP_PTR_MASK;
8183446Smrj 		id = pci_config_get8(cfg_hdl, ptr + PCI_CAP_ID);
8193446Smrj 
8203446Smrj 		if (id == PCI_CAP_ID_HT) {
8213446Smrj 			reg = pci_config_get16(cfg_hdl,
8223446Smrj 			    ptr + PCI_CAP_ID_REGS_OFF);
8233446Smrj 			if ((reg & reg_mask) == reg_val)
8243446Smrj 				return (ptr);
8253446Smrj 		}
8263446Smrj 		ptr = pci_config_get8(cfg_hdl, ptr + PCI_CAP_NEXT_PTR);
8273446Smrj 	}
8283446Smrj 
8293446Smrj 	return (PCI_CAP_NEXT_PTR_NULL);
8303446Smrj }
8313446Smrj 
8323446Smrj 
8333446Smrj static boolean_t
8343446Smrj ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl)
8353446Smrj {
8363446Smrj 	uint8_t ptr;
8373446Smrj 
8383446Smrj 	ptr = ppb_find_ht_cap(cfg_hdl,
8393446Smrj 	    PCI_CAP_HT_MSIMAP_TYPE_MASK | PCI_CAP_HT_MSIMAP_ENABLE_MASK,
8403446Smrj 	    PCI_CAP_HT_MSIMAP_TYPE | PCI_CAP_HT_MSIMAP_ENABLE);
8413446Smrj 
8423446Smrj 	if (ptr == PCI_CAP_NEXT_PTR_NULL)
8433446Smrj 		return (B_FALSE);
8443446Smrj 
8453446Smrj 	return (B_TRUE);
8463446Smrj }
8473446Smrj 
8483446Smrj 
8493446Smrj static int
8503446Smrj ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd)
8513446Smrj {
8523446Smrj 	uint8_t ptr;
8533446Smrj 	uint16_t reg;
8543446Smrj 
8553446Smrj 	ptr = ppb_find_ht_cap(cfg_hdl, PCI_CAP_HT_MSIMAP_TYPE_MASK,
8563446Smrj 	    PCI_CAP_HT_MSIMAP_TYPE);
8573446Smrj 	if (ptr == PCI_CAP_NEXT_PTR_NULL)
8583446Smrj 		return (0);
8593446Smrj 
8603446Smrj 	reg = pci_config_get16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF);
8613446Smrj 	switch (cmd) {
8623446Smrj 	case HT_MSIMAP_ENABLE:
8633446Smrj 		reg |= PCI_CAP_HT_MSIMAP_ENABLE;
8643446Smrj 		break;
8653446Smrj 	case HT_MSIMAP_DISABLE:
8663446Smrj 	default:
8673446Smrj 		reg &= ~(uint16_t)PCI_CAP_HT_MSIMAP_ENABLE;
8683446Smrj 	}
8693446Smrj 
8703446Smrj 	pci_config_put16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF, reg);
8713446Smrj 	return (1);
8723446Smrj }
8733446Smrj 
8743446Smrj 
8753446Smrj /*
8763446Smrj  * intercept certain interrupt services to handle special cases
8773446Smrj  */
8783446Smrj static int
8793446Smrj ppb_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
8803446Smrj     ddi_intr_handle_impl_t *hdlp, void *result)
8813446Smrj {
8823446Smrj 	ddi_acc_handle_t cfg_hdl;
8833446Smrj 	int rv = DDI_SUCCESS;
8843446Smrj 
8853446Smrj 	if (intr_op != DDI_INTROP_SUPPORTED_TYPES)
8863446Smrj 		return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result));
8873446Smrj 
8883446Smrj 	DDI_INTR_NEXDBG((CE_CONT,
8893446Smrj 	    "ppb_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
8903446Smrj 	    (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
8913446Smrj 
8923446Smrj 	/* Fixed interrupt is supported by default */
8933446Smrj 	*(int *)result = DDI_INTR_TYPE_FIXED;
8943446Smrj 
8953446Smrj 	if (ppb_support_msi == -1) {
8963446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
8973446Smrj 		    "ppb_intr_ops: MSI is not allowed\n"));
8983446Smrj 		goto OUT;
8993446Smrj 	}
9003446Smrj 
9013446Smrj 	if (ppb_support_msi == 1) {
9023446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
9033446Smrj 		    "ppb_intr_ops: MSI is always allowed\n"));
9043446Smrj 		rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result);
9053446Smrj 		goto OUT;
9063446Smrj 	}
9073446Smrj 
9083446Smrj 	if (pci_config_setup(pdip, &cfg_hdl) != DDI_SUCCESS) {
9093446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
9103446Smrj 		    "ppb_intr_ops: pci_config_setup() failed\n"));
9113446Smrj 		goto OUT;
9123446Smrj 	}
9133446Smrj 
9143446Smrj 	/*
9153446Smrj 	 * check for hypertransport msi mapping capability
9163446Smrj 	 */
9173446Smrj 	if (ppb_ht_msimap_check(cfg_hdl)) {
9183446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
9193446Smrj 		    "ppb_intr_ops: HT MSI mapping enabled\n"));
9203446Smrj 		rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result);
9213446Smrj 	}
9223446Smrj 
9233446Smrj 	/*
9243446Smrj 	 * if we add failure conditions after pci_config_setup, move this to
9253446Smrj 	 * OUT and use an extra flag to indicate the need to teardown cfg_hdl
9263446Smrj 	 */
9273446Smrj 	pci_config_teardown(&cfg_hdl);
9283446Smrj 
9293446Smrj OUT:
9303446Smrj 	DDI_INTR_NEXDBG((CE_CONT,
9313446Smrj 	    "ppb_intr_ops: rdip 0x%p, returns supported types: 0x%x\n",
9323446Smrj 	    (void *)rdip, *(int *)result));
9333446Smrj 	return (rv);
9343446Smrj }
9353446Smrj 
9363446Smrj static int
9373446Smrj ppb_open(dev_t *devp, int flags, int otyp, cred_t *credp)
9383446Smrj {
9393446Smrj 	return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp));
9403446Smrj }
9413446Smrj 
9423446Smrj static int
9433446Smrj ppb_close(dev_t dev, int flags, int otyp, cred_t *credp)
9443446Smrj {
9453446Smrj 	return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp));
9463446Smrj }
9473446Smrj 
9483446Smrj static int
9493446Smrj ppb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
9503446Smrj {
9513446Smrj 	return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, credp,
9523446Smrj 	    rvalp));
9533446Smrj }
9543446Smrj 
9553446Smrj static int
9563446Smrj ppb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
9573446Smrj 	int flags, char *name, caddr_t valuep, int *lengthp)
9583446Smrj {
9593446Smrj 	return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags,
9603446Smrj 	    name, valuep, lengthp));
9613446Smrj }
9623446Smrj 
9633446Smrj static int
9643446Smrj ppb_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
9653446Smrj {
9663446Smrj 	return (pcihp_info(dip, cmd, arg, result));
9673446Smrj }
9683446Smrj 
9696313Skrishnae void ppb_peekpoke_cb(dev_info_t *dip, ddi_fm_error_t *derr) {
9706313Skrishnae 	(void) pci_ereport_post(dip, derr, NULL);
9716313Skrishnae }
9726313Skrishnae 
9733446Smrj /*ARGSUSED*/
9743446Smrj static int
9753446Smrj ppb_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap,
9763446Smrj     ddi_iblock_cookie_t *ibc)
9773446Smrj {
9783446Smrj 	ppb_devstate_t  *ppb = ddi_get_soft_state(ppb_state,
9793446Smrj 	    ddi_get_instance(dip));
9803446Smrj 
9813446Smrj 	ASSERT(ibc != NULL);
9823446Smrj 	*ibc = ppb->ppb_fm_ibc;
9833446Smrj 
9843446Smrj 	return (ppb->ppb_fmcap);
9853446Smrj }
9863446Smrj 
9873446Smrj /*ARGSUSED*/
9883446Smrj static int
9893446Smrj ppb_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *no_used)
9903446Smrj {
9913446Smrj 	ppb_devstate_t  *ppb = ddi_get_soft_state(ppb_state,
9923446Smrj 	    ddi_get_instance(dip));
9933446Smrj 
9943446Smrj 	mutex_enter(&ppb->ppb_err_mutex);
9953446Smrj 	pci_ereport_post(dip, derr, NULL);
9963446Smrj 	mutex_exit(&ppb->ppb_err_mutex);
9973446Smrj 	return (derr->fme_status);
9983446Smrj }
999