xref: /onnv-gate/usr/src/uts/intel/io/pci/pci_pci.c (revision 7656)
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 */
175*7656SSherry.Moore@Sun.COM 	&ppb_bus_ops,		/* bus operations */
176*7656SSherry.Moore@Sun.COM 	NULL,			/* power */
177*7656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* quiesce */
1783446Smrj };
1793446Smrj 
1803446Smrj /*
1813446Smrj  * Module linkage information for the kernel.
1823446Smrj  */
1833446Smrj 
1843446Smrj static struct modldrv modldrv = {
1853446Smrj 	&mod_driverops, /* Type of module */
1867542SRichard.Bean@Sun.COM 	"PCI to PCI bridge nexus driver",
1873446Smrj 	&ppb_ops,	/* driver ops */
1883446Smrj };
1893446Smrj 
1903446Smrj static struct modlinkage modlinkage = {
1913446Smrj 	MODREV_1,
1923446Smrj 	(void *)&modldrv,
1933446Smrj 	NULL
1943446Smrj };
1953446Smrj 
1963446Smrj /*
1973446Smrj  * soft state pointer and structure template:
1983446Smrj  */
1993446Smrj static void *ppb_state;
2003446Smrj 
2013446Smrj typedef struct {
2023446Smrj 	dev_info_t *dip;
2033446Smrj 	int ppb_fmcap;
2043446Smrj 	ddi_iblock_cookie_t ppb_fm_ibc;
2053446Smrj 	kmutex_t ppb_peek_poke_mutex;
2063446Smrj 	kmutex_t ppb_err_mutex;
2073446Smrj 
2083446Smrj 	/*
2093446Smrj 	 * cpr support:
2103446Smrj 	 */
2113446Smrj 	uint_t config_state_index;
2123446Smrj 	struct {
2133446Smrj 		dev_info_t *dip;
2143446Smrj 		ushort_t command;
2153446Smrj 		uchar_t cache_line_size;
2163446Smrj 		uchar_t latency_timer;
2173446Smrj 		uchar_t header_type;
2183446Smrj 		uchar_t sec_latency_timer;
2193446Smrj 		ushort_t bridge_control;
2203446Smrj 	} config_state[PCI_MAX_CHILDREN];
2216313Skrishnae 
2226313Skrishnae 	uint8_t parent_bus;
2233446Smrj } ppb_devstate_t;
2243446Smrj 
2253446Smrj 
2263446Smrj /*
2273446Smrj  * forward function declarations:
2283446Smrj  */
2293446Smrj static void	ppb_removechild(dev_info_t *);
2303446Smrj static int	ppb_initchild(dev_info_t *child);
2313446Smrj static void	ppb_save_config_regs(ppb_devstate_t *ppb_p);
2323446Smrj static void	ppb_restore_config_regs(ppb_devstate_t *ppb_p);
2333446Smrj static uint8_t	ppb_find_ht_cap(ddi_acc_handle_t cfg_hdl, uint16_t reg_mask,
2343446Smrj 		    uint16_t reg_val);
2353446Smrj static boolean_t	ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl);
2363446Smrj static int	ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd);
2373446Smrj 
2383446Smrj /*
2393446Smrj  * for <cmd> in ppb_ht_msimap_set
2403446Smrj  */
2413446Smrj #define	HT_MSIMAP_ENABLE	1
2423446Smrj #define	HT_MSIMAP_DISABLE	0
2433446Smrj 
2443446Smrj 
2453446Smrj int
2463446Smrj _init(void)
2473446Smrj {
2483446Smrj 	int e;
2493446Smrj 
2503446Smrj 	if ((e = ddi_soft_state_init(&ppb_state, sizeof (ppb_devstate_t),
2513446Smrj 	    1)) == 0 && (e = mod_install(&modlinkage)) != 0)
2523446Smrj 		ddi_soft_state_fini(&ppb_state);
2533446Smrj 	return (e);
2543446Smrj }
2553446Smrj 
2563446Smrj int
2573446Smrj _fini(void)
2583446Smrj {
2593446Smrj 	int e;
2603446Smrj 
2613446Smrj 	if ((e = mod_remove(&modlinkage)) == 0)
2623446Smrj 		ddi_soft_state_fini(&ppb_state);
2633446Smrj 	return (e);
2643446Smrj }
2653446Smrj 
2663446Smrj int
2673446Smrj _info(struct modinfo *modinfop)
2683446Smrj {
2693446Smrj 	return (mod_info(&modlinkage, modinfop));
2703446Smrj }
2713446Smrj 
2723446Smrj /*ARGSUSED*/
2733446Smrj static int
2743446Smrj ppb_probe(dev_info_t *devi)
2753446Smrj {
2763446Smrj 	return (DDI_PROBE_SUCCESS);
2773446Smrj }
2783446Smrj 
2793446Smrj /*ARGSUSED*/
2803446Smrj static int
2813446Smrj ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
2823446Smrj {
2836313Skrishnae 	dev_info_t *root = ddi_root_node();
2843446Smrj 	int instance;
2853446Smrj 	ppb_devstate_t *ppb;
2866313Skrishnae 	dev_info_t *pdip;
2873446Smrj 	ddi_acc_handle_t config_handle;
2886313Skrishnae 	char *bus;
2893446Smrj 
2903446Smrj 	switch (cmd) {
2913446Smrj 	case DDI_ATTACH:
2923446Smrj 
2933446Smrj 		/*
2943446Smrj 		 * Make sure the "device_type" property exists.
2953446Smrj 		 */
2963446Smrj 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
2973446Smrj 		    "device_type", "pci");
2983446Smrj 
2993446Smrj 		/*
3003446Smrj 		 * Allocate and get soft state structure.
3013446Smrj 		 */
3023446Smrj 		instance = ddi_get_instance(devi);
3033446Smrj 		if (ddi_soft_state_zalloc(ppb_state, instance) != DDI_SUCCESS)
3043446Smrj 			return (DDI_FAILURE);
3053446Smrj 		ppb = ddi_get_soft_state(ppb_state, instance);
3063446Smrj 		ppb->dip = devi;
3073446Smrj 
3083446Smrj 		/*
3093446Smrj 		 * don't enable ereports if immediate child of npe
3103446Smrj 		 */
3113446Smrj 		if (strcmp(ddi_driver_name(ddi_get_parent(devi)), "npe") == 0)
3123446Smrj 			ppb->ppb_fmcap = DDI_FM_ERRCB_CAPABLE |
3133446Smrj 			    DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
3143446Smrj 		else
3153446Smrj 			ppb->ppb_fmcap = DDI_FM_EREPORT_CAPABLE |
3163446Smrj 			    DDI_FM_ERRCB_CAPABLE | DDI_FM_ACCCHK_CAPABLE |
3173446Smrj 			    DDI_FM_DMACHK_CAPABLE;
3183446Smrj 
3193446Smrj 		ddi_fm_init(devi, &ppb->ppb_fmcap, &ppb->ppb_fm_ibc);
3203446Smrj 		mutex_init(&ppb->ppb_err_mutex, NULL, MUTEX_DRIVER,
3213446Smrj 		    (void *)ppb->ppb_fm_ibc);
3223446Smrj 		mutex_init(&ppb->ppb_peek_poke_mutex, NULL, MUTEX_DRIVER,
3233446Smrj 		    (void *)ppb->ppb_fm_ibc);
3243446Smrj 
3253446Smrj 		if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
3263446Smrj 		    DDI_FM_EREPORT_CAPABLE))
3273446Smrj 			pci_ereport_setup(devi);
3283446Smrj 		if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
3295295Srandyf 			ddi_fm_handler_register(devi, ppb_fm_callback, NULL);
3303446Smrj 
3313446Smrj 		if (pci_config_setup(devi, &config_handle) != DDI_SUCCESS) {
3323446Smrj 			if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
3333446Smrj 				ddi_fm_handler_unregister(devi);
3343446Smrj 			if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
3353446Smrj 			    DDI_FM_EREPORT_CAPABLE))
3363446Smrj 				pci_ereport_teardown(devi);
3373446Smrj 			ddi_fm_fini(devi);
3383446Smrj 			ddi_soft_state_free(ppb_state, instance);
3393446Smrj 			return (DDI_FAILURE);
3403446Smrj 		}
3413446Smrj 
3426313Skrishnae 		ppb->parent_bus = PCIE_PCIECAP_DEV_TYPE_PCI_DEV;
3436313Skrishnae 		for (pdip = ddi_get_parent(devi); pdip && (pdip != root) &&
3446313Skrishnae 		    (ppb->parent_bus != PCIE_PCIECAP_DEV_TYPE_PCIE_DEV);
3456313Skrishnae 		    pdip = ddi_get_parent(pdip)) {
3466313Skrishnae 			if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip,
3476313Skrishnae 			    DDI_PROP_DONTPASS, "device_type", &bus) !=
3486313Skrishnae 			    DDI_PROP_SUCCESS)
3496313Skrishnae 				break;
3506313Skrishnae 
3516313Skrishnae 			if (strcmp(bus, "pciex") == 0)
3526313Skrishnae 				ppb->parent_bus =
3536313Skrishnae 				    PCIE_PCIECAP_DEV_TYPE_PCIE_DEV;
3546313Skrishnae 
3556313Skrishnae 			ddi_prop_free(bus);
3566313Skrishnae 		}
3576313Skrishnae 
3583446Smrj 		if (ppb_support_ht_msimap == 1)
3593446Smrj 			(void) ppb_ht_msimap_set(config_handle,
3603446Smrj 			    HT_MSIMAP_ENABLE);
3613446Smrj 		else if (ppb_support_ht_msimap == -1)
3623446Smrj 			(void) ppb_ht_msimap_set(config_handle,
3633446Smrj 			    HT_MSIMAP_DISABLE);
3643446Smrj 
3653446Smrj 		pci_config_teardown(&config_handle);
3663446Smrj 
3673446Smrj 		/*
3683446Smrj 		 * Initialize hotplug support on this bus. At minimum
3693446Smrj 		 * (for non hotplug bus) this would create ":devctl" minor
3703446Smrj 		 * node to support DEVCTL_DEVICE_* and DEVCTL_BUS_* ioctls
3713446Smrj 		 * to this bus.
3723446Smrj 		 */
3733446Smrj 		if (pcihp_init(devi) != DDI_SUCCESS)
3745295Srandyf 			cmn_err(CE_WARN,
3755295Srandyf 			    "pci: Failed to setup hotplug framework");
3763446Smrj 
3773446Smrj 		ddi_report_dev(devi);
3783446Smrj 		return (DDI_SUCCESS);
3793446Smrj 
3803446Smrj 	case DDI_RESUME:
3813446Smrj 
3823446Smrj 		/*
3833446Smrj 		 * Get the soft state structure for the bridge.
3843446Smrj 		 */
3853446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
3863446Smrj 		ppb_restore_config_regs(ppb);
3873446Smrj 		return (DDI_SUCCESS);
3883446Smrj 
3893446Smrj 	default:
3903446Smrj 		break;
3913446Smrj 	}
3923446Smrj 	return (DDI_FAILURE);
3933446Smrj }
3943446Smrj 
3953446Smrj /*ARGSUSED*/
3963446Smrj static int
3973446Smrj ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
3983446Smrj {
3993446Smrj 	ppb_devstate_t *ppb;
4003446Smrj 
4013446Smrj 	switch (cmd) {
4023446Smrj 	case DDI_DETACH:
4033446Smrj 		(void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type");
4043446Smrj 
4053446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
4063446Smrj 		if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
4073446Smrj 			ddi_fm_handler_unregister(devi);
4083446Smrj 		if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
4093446Smrj 		    DDI_FM_EREPORT_CAPABLE))
4103446Smrj 			pci_ereport_teardown(devi);
4113446Smrj 		mutex_destroy(&ppb->ppb_peek_poke_mutex);
4123446Smrj 		mutex_destroy(&ppb->ppb_err_mutex);
4133446Smrj 		ddi_fm_fini(devi);
4143446Smrj 
4153446Smrj 		/*
4163446Smrj 		 * And finally free the per-pci soft state.
4173446Smrj 		 */
4183446Smrj 		ddi_soft_state_free(ppb_state, ddi_get_instance(devi));
4193446Smrj 
4203446Smrj 		/*
4213446Smrj 		 * Uninitialize hotplug support on this bus.
4223446Smrj 		 */
4233446Smrj 		(void) pcihp_uninit(devi);
4243446Smrj 		return (DDI_SUCCESS);
4253446Smrj 
4263446Smrj 	case DDI_SUSPEND:
4273446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
4283446Smrj 		ppb_save_config_regs(ppb);
4293446Smrj 		return (DDI_SUCCESS);
4303446Smrj 
4313446Smrj 	default:
4323446Smrj 		break;
4333446Smrj 	}
4343446Smrj 	return (DDI_FAILURE);
4353446Smrj }
4363446Smrj 
4373446Smrj /*ARGSUSED*/
4383446Smrj static int
4393446Smrj ppb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
4403446Smrj 	off_t offset, off_t len, caddr_t *vaddrp)
4413446Smrj {
4423446Smrj 	dev_info_t *pdip;
4433446Smrj 
4443446Smrj 	pdip = (dev_info_t *)DEVI(dip)->devi_parent;
4453446Smrj 	return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip,
4465295Srandyf 	    rdip, mp, offset, len, vaddrp));
4473446Smrj }
4483446Smrj 
4493446Smrj /*ARGSUSED*/
4503446Smrj static int
4513446Smrj ppb_ctlops(dev_info_t *dip, dev_info_t *rdip,
4523446Smrj 	ddi_ctl_enum_t ctlop, void *arg, void *result)
4533446Smrj {
4543446Smrj 	pci_regspec_t *drv_regp;
4553446Smrj 	int	reglen;
4563446Smrj 	int	rn;
4573446Smrj 	int	totreg;
4586313Skrishnae 	ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state,
4596313Skrishnae 	    ddi_get_instance(dip));
4606313Skrishnae 	struct detachspec *dsp;
4615295Srandyf 	struct attachspec *asp;
4623446Smrj 
4633446Smrj 	switch (ctlop) {
4643446Smrj 	case DDI_CTLOPS_REPORTDEV:
4653446Smrj 		if (rdip == (dev_info_t *)0)
4663446Smrj 			return (DDI_FAILURE);
4673446Smrj 		cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n",
4683446Smrj 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
4693446Smrj 		    ddi_driver_name(rdip),
4703446Smrj 		    ddi_get_instance(rdip));
4713446Smrj 		return (DDI_SUCCESS);
4723446Smrj 
4733446Smrj 	case DDI_CTLOPS_INITCHILD:
4743446Smrj 		return (ppb_initchild((dev_info_t *)arg));
4753446Smrj 
4763446Smrj 	case DDI_CTLOPS_UNINITCHILD:
4773446Smrj 		ppb_removechild((dev_info_t *)arg);
4783446Smrj 		return (DDI_SUCCESS);
4793446Smrj 
4803446Smrj 	case DDI_CTLOPS_SIDDEV:
4813446Smrj 		return (DDI_SUCCESS);
4823446Smrj 
4833446Smrj 	case DDI_CTLOPS_REGSIZE:
4843446Smrj 	case DDI_CTLOPS_NREGS:
4853446Smrj 		if (rdip == (dev_info_t *)0)
4863446Smrj 			return (DDI_FAILURE);
4873446Smrj 		break;
4883446Smrj 
4895295Srandyf 	/* X86 systems support PME wakeup from suspend */
4905295Srandyf 	case DDI_CTLOPS_ATTACH:
4916313Skrishnae 		if (!pcie_is_child(dip, rdip))
4926313Skrishnae 			return (DDI_SUCCESS);
4936313Skrishnae 
4945295Srandyf 		asp = (struct attachspec *)arg;
4956313Skrishnae 		if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) &&
4966313Skrishnae 		    (asp->when == DDI_POST) && (asp->result == DDI_SUCCESS))
4976313Skrishnae 			pf_init(rdip, (void *)ppb->ppb_fm_ibc, asp->cmd);
4986313Skrishnae 
4995295Srandyf 		if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE)
5005295Srandyf 			if (pci_pre_resume(rdip) != DDI_SUCCESS)
5015295Srandyf 				return (DDI_FAILURE);
5025295Srandyf 
5036313Skrishnae 		return (DDI_SUCCESS);
5045295Srandyf 
5055295Srandyf 	case DDI_CTLOPS_DETACH:
5066313Skrishnae 		if (!pcie_is_child(dip, rdip))
5076313Skrishnae 			return (DDI_SUCCESS);
5086313Skrishnae 
5096313Skrishnae 		dsp = (struct detachspec *)arg;
5106313Skrishnae 		if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) &&
5116313Skrishnae 		    (dsp->when == DDI_PRE))
5126313Skrishnae 			pf_fini(rdip, dsp->cmd);
5136313Skrishnae 
5146313Skrishnae 		if (dsp->cmd == DDI_SUSPEND && dsp->when == DDI_POST)
5155295Srandyf 			if (pci_post_suspend(rdip) != DDI_SUCCESS)
5165295Srandyf 				return (DDI_FAILURE);
5176313Skrishnae 
5186313Skrishnae 		return (DDI_SUCCESS);
5195295Srandyf 
5203446Smrj 	case DDI_CTLOPS_PEEK:
5213446Smrj 	case DDI_CTLOPS_POKE:
5223446Smrj 		if (strcmp(ddi_driver_name(ddi_get_parent(dip)), "npe") != 0)
5233446Smrj 			return (ddi_ctlops(dip, rdip, ctlop, arg, result));
5243446Smrj 		return (pci_peekpoke_check(dip, rdip, ctlop, arg, result,
5253446Smrj 		    ddi_ctlops, &ppb->ppb_err_mutex,
5266313Skrishnae 		    &ppb->ppb_peek_poke_mutex, ppb_peekpoke_cb));
5273446Smrj 
5283446Smrj 	default:
5293446Smrj 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
5303446Smrj 	}
5313446Smrj 
5323446Smrj 	*(int *)result = 0;
5333446Smrj 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip,
5345295Srandyf 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg",
5355295Srandyf 	    (caddr_t)&drv_regp, &reglen) != DDI_SUCCESS)
5363446Smrj 		return (DDI_FAILURE);
5373446Smrj 
5383446Smrj 	totreg = reglen / sizeof (pci_regspec_t);
5393446Smrj 	if (ctlop == DDI_CTLOPS_NREGS)
5403446Smrj 		*(int *)result = totreg;
5413446Smrj 	else if (ctlop == DDI_CTLOPS_REGSIZE) {
5423446Smrj 		rn = *(int *)arg;
5433446Smrj 		if (rn >= totreg) {
5443446Smrj 			kmem_free(drv_regp, reglen);
5453446Smrj 			return (DDI_FAILURE);
5463446Smrj 		}
5473446Smrj 		*(off_t *)result = drv_regp[rn].pci_size_low;
5483446Smrj 	}
5493446Smrj 
5503446Smrj 	kmem_free(drv_regp, reglen);
5513446Smrj 	return (DDI_SUCCESS);
5523446Smrj }
5533446Smrj 
5543446Smrj static int
5553446Smrj ppb_name_child(dev_info_t *child, char *name, int namelen)
5563446Smrj {
5573446Smrj 	pci_regspec_t *pci_rp;
5583446Smrj 	uint_t slot, func;
5593446Smrj 	char **unit_addr;
5603446Smrj 	uint_t n;
5613446Smrj 
5623446Smrj 	/*
5633446Smrj 	 * For .conf nodes, use unit-address property as name
5643446Smrj 	 */
5653446Smrj 	if (ndi_dev_is_persistent_node(child) == 0) {
5663446Smrj 		if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
5673446Smrj 		    DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) !=
5683446Smrj 		    DDI_PROP_SUCCESS) {
5693446Smrj 			cmn_err(CE_WARN,
5703446Smrj 			    "cannot find unit-address in %s.conf",
5713446Smrj 			    ddi_driver_name(child));
5723446Smrj 			return (DDI_FAILURE);
5733446Smrj 		}
5743446Smrj 		if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
5753446Smrj 			cmn_err(CE_WARN, "unit-address property in %s.conf"
5763446Smrj 			    " not well-formed", ddi_driver_name(child));
5773446Smrj 			ddi_prop_free(unit_addr);
5783446Smrj 			return (DDI_SUCCESS);
5793446Smrj 		}
5803446Smrj 		(void) snprintf(name, namelen, "%s", *unit_addr);
5813446Smrj 		ddi_prop_free(unit_addr);
5823446Smrj 		return (DDI_SUCCESS);
5833446Smrj 	}
5843446Smrj 
5853446Smrj 	/* get child "reg" property */
5863446Smrj 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
5873446Smrj 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) {
5883446Smrj 		return (DDI_FAILURE);
5893446Smrj 	}
5903446Smrj 
5913446Smrj 	/* copy the device identifications */
5923446Smrj 	slot = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
5933446Smrj 	func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
5943446Smrj 
5953446Smrj 	if (func != 0)
5963446Smrj 		(void) snprintf(name, namelen, "%x,%x", slot, func);
5973446Smrj 	else
5983446Smrj 		(void) snprintf(name, namelen, "%x", slot);
5993446Smrj 
6003446Smrj 	ddi_prop_free(pci_rp);
6013446Smrj 	return (DDI_SUCCESS);
6023446Smrj }
6033446Smrj 
6043446Smrj static int
6053446Smrj ppb_initchild(dev_info_t *child)
6063446Smrj {
6073446Smrj 	struct ddi_parent_private_data *pdptr;
6086313Skrishnae 	ppb_devstate_t *ppb;
6093446Smrj 	char name[MAXNAMELEN];
6103446Smrj 	ddi_acc_handle_t config_handle;
6113446Smrj 	ushort_t command_preserve, command;
6123446Smrj 
6136313Skrishnae 	ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
6146313Skrishnae 	    ddi_get_instance(ddi_get_parent(child)));
6156313Skrishnae 
6163446Smrj 	if (ppb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS)
6173446Smrj 		return (DDI_FAILURE);
6183446Smrj 	ddi_set_name_addr(child, name);
6193446Smrj 
6203446Smrj 	/*
6213446Smrj 	 * Pseudo nodes indicate a prototype node with per-instance
6223446Smrj 	 * properties to be merged into the real h/w device node.
6233446Smrj 	 * The interpretation of the unit-address is DD[,F]
6243446Smrj 	 * where DD is the device id and F is the function.
6253446Smrj 	 */
6263446Smrj 	if (ndi_dev_is_persistent_node(child) == 0) {
6273446Smrj 		extern int pci_allow_pseudo_children;
6283446Smrj 
6293446Smrj 		ddi_set_parent_data(child, NULL);
6303446Smrj 
6313446Smrj 		/*
6323446Smrj 		 * Try to merge the properties from this prototype
6333446Smrj 		 * node into real h/w nodes.
6343446Smrj 		 */
6353446Smrj 		if (ndi_merge_node(child, ppb_name_child) == DDI_SUCCESS) {
6363446Smrj 			/*
6373446Smrj 			 * Merged ok - return failure to remove the node.
6383446Smrj 			 */
6393446Smrj 			ddi_set_name_addr(child, NULL);
6403446Smrj 			return (DDI_FAILURE);
6413446Smrj 		}
6423446Smrj 
6433446Smrj 		/* workaround for ddivs to run under PCI */
6443446Smrj 		if (pci_allow_pseudo_children)
6453446Smrj 			return (DDI_SUCCESS);
6463446Smrj 
6473446Smrj 		/*
6483446Smrj 		 * The child was not merged into a h/w node,
6493446Smrj 		 * but there's not much we can do with it other
6503446Smrj 		 * than return failure to cause the node to be removed.
6513446Smrj 		 */
6523446Smrj 		cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
6533446Smrj 		    ddi_driver_name(child), ddi_get_name_addr(child),
6543446Smrj 		    ddi_driver_name(child));
6553446Smrj 		ddi_set_name_addr(child, NULL);
6563446Smrj 		return (DDI_NOT_WELL_FORMED);
6573446Smrj 	}
6583446Smrj 
6596313Skrishnae 	ddi_set_parent_data(child, NULL);
6606313Skrishnae 
6616313Skrishnae 	/*
6626313Skrishnae 	 * PCIe FMA specific
6636313Skrishnae 	 *
6646313Skrishnae 	 * Note: parent_data for parent is created only if this is PCI-E
6656313Skrishnae 	 * platform, for which, SG take a different route to handle device
6666313Skrishnae 	 * errors.
6676313Skrishnae 	 */
6686313Skrishnae 	if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
6696313Skrishnae 		if (pcie_init_bus(child) == NULL)
6706313Skrishnae 			return (DDI_FAILURE);
6716313Skrishnae 	}
6726313Skrishnae 
6733446Smrj 	/* transfer select properties from PROM to kernel */
6745295Srandyf 	if (ddi_getprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS,
6755295Srandyf 	    "interrupts", -1) != -1) {
6763446Smrj 		pdptr = kmem_zalloc((sizeof (struct ddi_parent_private_data) +
6773446Smrj 		    sizeof (struct intrspec)), KM_SLEEP);
6783446Smrj 		pdptr->par_intr = (struct intrspec *)(pdptr + 1);
6793446Smrj 		pdptr->par_nintr = 1;
6803446Smrj 		ddi_set_parent_data(child, pdptr);
6813446Smrj 	} else
6823446Smrj 		ddi_set_parent_data(child, NULL);
6833446Smrj 
6843446Smrj 	if (pci_config_setup(child, &config_handle) != DDI_SUCCESS)
6853446Smrj 		return (DDI_FAILURE);
6863446Smrj 
6873446Smrj 	/*
6883446Smrj 	 * Support for the "command-preserve" property.
6893446Smrj 	 */
6903446Smrj 	command_preserve = ddi_prop_get_int(DDI_DEV_T_ANY, child,
6913446Smrj 	    DDI_PROP_DONTPASS, "command-preserve", 0);
6923446Smrj 	command = pci_config_get16(config_handle, PCI_CONF_COMM);
6933446Smrj 	command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB);
6943446Smrj 	command |= (ppb_command_default & ~command_preserve);
6953446Smrj 	pci_config_put16(config_handle, PCI_CONF_COMM, command);
6963446Smrj 
6973446Smrj 	pci_config_teardown(&config_handle);
6983446Smrj 	return (DDI_SUCCESS);
6993446Smrj }
7003446Smrj 
7013446Smrj static void
7023446Smrj ppb_removechild(dev_info_t *dip)
7033446Smrj {
7043446Smrj 	struct ddi_parent_private_data *pdptr;
7056313Skrishnae 	ppb_devstate_t *ppb;
7063446Smrj 
7076313Skrishnae 	ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
7086313Skrishnae 	    ddi_get_instance(ddi_get_parent(dip)));
7096313Skrishnae 
7106313Skrishnae 	if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
7116313Skrishnae 		pcie_fini_bus(dip);
7126313Skrishnae 	else if ((pdptr = ddi_get_parent_data(dip)) != NULL) {
7133446Smrj 		kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec)));
7143446Smrj 		ddi_set_parent_data(dip, NULL);
7153446Smrj 	}
7163446Smrj 	ddi_set_name_addr(dip, NULL);
7173446Smrj 
7183446Smrj 	/*
7193446Smrj 	 * Strip the node to properly convert it back to prototype form
7203446Smrj 	 */
7213446Smrj 	ddi_remove_minor_node(dip, NULL);
7223446Smrj 
7233446Smrj 	impl_rem_dev_props(dip);
7243446Smrj }
7253446Smrj 
7263446Smrj /*
7273446Smrj  * ppb_save_config_regs
7283446Smrj  *
7293446Smrj  * This routine saves the state of the configuration registers of all
7303446Smrj  * the child nodes of each PBM.
7313446Smrj  *
7323446Smrj  * used by: ppb_detach() on suspends
7333446Smrj  *
7343446Smrj  * return value: none
7353446Smrj  */
7363446Smrj static void
7373446Smrj ppb_save_config_regs(ppb_devstate_t *ppb_p)
7383446Smrj {
7393446Smrj 	int i;
7403446Smrj 	dev_info_t *dip;
7413446Smrj 	ddi_acc_handle_t config_handle;
7423446Smrj 
7433446Smrj 	for (i = 0, dip = ddi_get_child(ppb_p->dip); dip != NULL;
7445295Srandyf 	    i++, dip = ddi_get_next_sibling(dip)) {
7453446Smrj 
7463446Smrj 		if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
7473446Smrj 			cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
7485295Srandyf 			    ddi_driver_name(ppb_p->dip),
7495295Srandyf 			    ddi_get_instance(ppb_p->dip),
7505295Srandyf 			    ddi_driver_name(dip),
7515295Srandyf 			    ddi_get_instance(dip));
7523446Smrj 			continue;
7533446Smrj 		}
7543446Smrj 
7553446Smrj 		ppb_p->config_state[i].dip = dip;
7563446Smrj 		ppb_p->config_state[i].command =
7575295Srandyf 		    pci_config_get16(config_handle, PCI_CONF_COMM);
7583446Smrj 		pci_config_teardown(&config_handle);
7593446Smrj 	}
7603446Smrj 	ppb_p->config_state_index = i;
7613446Smrj }
7623446Smrj 
7633446Smrj 
7643446Smrj /*
7653446Smrj  * ppb_restore_config_regs
7663446Smrj  *
7673446Smrj  * This routine restores the state of the configuration registers of all
7683446Smrj  * the child nodes of each PBM.
7693446Smrj  *
7703446Smrj  * used by: ppb_attach() on resume
7713446Smrj  *
7723446Smrj  * return value: none
7733446Smrj  */
7743446Smrj static void
7753446Smrj ppb_restore_config_regs(ppb_devstate_t *ppb_p)
7763446Smrj {
7773446Smrj 	int i;
7783446Smrj 	dev_info_t *dip;
7793446Smrj 	ddi_acc_handle_t config_handle;
7803446Smrj 
7813446Smrj 	for (i = 0; i < ppb_p->config_state_index; i++) {
7823446Smrj 		dip = ppb_p->config_state[i].dip;
7833446Smrj 		if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
7843446Smrj 			cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
7855295Srandyf 			    ddi_driver_name(ppb_p->dip),
7865295Srandyf 			    ddi_get_instance(ppb_p->dip),
7875295Srandyf 			    ddi_driver_name(dip),
7885295Srandyf 			    ddi_get_instance(dip));
7893446Smrj 			continue;
7903446Smrj 		}
7913446Smrj 		pci_config_put16(config_handle, PCI_CONF_COMM,
7925295Srandyf 		    ppb_p->config_state[i].command);
7933446Smrj 		pci_config_teardown(&config_handle);
7943446Smrj 	}
7953446Smrj }
7963446Smrj 
7973446Smrj 
7983446Smrj /*
7993446Smrj  * returns the location of a hypertransport capability whose upper 16-bit
8003446Smrj  * register of the cap header matches <reg_val> after masking the register
8013446Smrj  * with <reg_mask>; if both <reg_mask> and <reg_val> are 0, it will return the
8023446Smrj  * first HT cap found
8033446Smrj  */
8043446Smrj static uint8_t
8053446Smrj ppb_find_ht_cap(ddi_acc_handle_t cfg_hdl, uint16_t reg_mask, uint16_t reg_val)
8063446Smrj {
8073446Smrj 	uint16_t status, reg;
8083446Smrj 	uint8_t ptr, id;
8093446Smrj 
8103446Smrj 	status = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
8113446Smrj 	if (status == 0xffff || !((status & PCI_STAT_CAP)))
8123446Smrj 		return (PCI_CAP_NEXT_PTR_NULL);
8133446Smrj 
8143446Smrj 	ptr = pci_config_get8(cfg_hdl, PCI_CONF_CAP_PTR);
8153446Smrj 	while (ptr != 0xFF &&
8163446Smrj 	    ptr != PCI_CAP_NEXT_PTR_NULL &&
8173446Smrj 	    ptr >= PCI_CAP_PTR_OFF) {
8183446Smrj 
8193446Smrj 		ptr &= PCI_CAP_PTR_MASK;
8203446Smrj 		id = pci_config_get8(cfg_hdl, ptr + PCI_CAP_ID);
8213446Smrj 
8223446Smrj 		if (id == PCI_CAP_ID_HT) {
8233446Smrj 			reg = pci_config_get16(cfg_hdl,
8243446Smrj 			    ptr + PCI_CAP_ID_REGS_OFF);
8253446Smrj 			if ((reg & reg_mask) == reg_val)
8263446Smrj 				return (ptr);
8273446Smrj 		}
8283446Smrj 		ptr = pci_config_get8(cfg_hdl, ptr + PCI_CAP_NEXT_PTR);
8293446Smrj 	}
8303446Smrj 
8313446Smrj 	return (PCI_CAP_NEXT_PTR_NULL);
8323446Smrj }
8333446Smrj 
8343446Smrj 
8353446Smrj static boolean_t
8363446Smrj ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl)
8373446Smrj {
8383446Smrj 	uint8_t ptr;
8393446Smrj 
8403446Smrj 	ptr = ppb_find_ht_cap(cfg_hdl,
8413446Smrj 	    PCI_CAP_HT_MSIMAP_TYPE_MASK | PCI_CAP_HT_MSIMAP_ENABLE_MASK,
8423446Smrj 	    PCI_CAP_HT_MSIMAP_TYPE | PCI_CAP_HT_MSIMAP_ENABLE);
8433446Smrj 
8443446Smrj 	if (ptr == PCI_CAP_NEXT_PTR_NULL)
8453446Smrj 		return (B_FALSE);
8463446Smrj 
8473446Smrj 	return (B_TRUE);
8483446Smrj }
8493446Smrj 
8503446Smrj 
8513446Smrj static int
8523446Smrj ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd)
8533446Smrj {
8543446Smrj 	uint8_t ptr;
8553446Smrj 	uint16_t reg;
8563446Smrj 
8573446Smrj 	ptr = ppb_find_ht_cap(cfg_hdl, PCI_CAP_HT_MSIMAP_TYPE_MASK,
8583446Smrj 	    PCI_CAP_HT_MSIMAP_TYPE);
8593446Smrj 	if (ptr == PCI_CAP_NEXT_PTR_NULL)
8603446Smrj 		return (0);
8613446Smrj 
8623446Smrj 	reg = pci_config_get16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF);
8633446Smrj 	switch (cmd) {
8643446Smrj 	case HT_MSIMAP_ENABLE:
8653446Smrj 		reg |= PCI_CAP_HT_MSIMAP_ENABLE;
8663446Smrj 		break;
8673446Smrj 	case HT_MSIMAP_DISABLE:
8683446Smrj 	default:
8693446Smrj 		reg &= ~(uint16_t)PCI_CAP_HT_MSIMAP_ENABLE;
8703446Smrj 	}
8713446Smrj 
8723446Smrj 	pci_config_put16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF, reg);
8733446Smrj 	return (1);
8743446Smrj }
8753446Smrj 
8763446Smrj 
8773446Smrj /*
8783446Smrj  * intercept certain interrupt services to handle special cases
8793446Smrj  */
8803446Smrj static int
8813446Smrj ppb_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
8823446Smrj     ddi_intr_handle_impl_t *hdlp, void *result)
8833446Smrj {
8843446Smrj 	ddi_acc_handle_t cfg_hdl;
8853446Smrj 	int rv = DDI_SUCCESS;
8863446Smrj 
8873446Smrj 	if (intr_op != DDI_INTROP_SUPPORTED_TYPES)
8883446Smrj 		return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result));
8893446Smrj 
8903446Smrj 	DDI_INTR_NEXDBG((CE_CONT,
8913446Smrj 	    "ppb_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
8923446Smrj 	    (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
8933446Smrj 
8943446Smrj 	/* Fixed interrupt is supported by default */
8953446Smrj 	*(int *)result = DDI_INTR_TYPE_FIXED;
8963446Smrj 
8973446Smrj 	if (ppb_support_msi == -1) {
8983446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
8993446Smrj 		    "ppb_intr_ops: MSI is not allowed\n"));
9003446Smrj 		goto OUT;
9013446Smrj 	}
9023446Smrj 
9033446Smrj 	if (ppb_support_msi == 1) {
9043446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
9053446Smrj 		    "ppb_intr_ops: MSI is always allowed\n"));
9063446Smrj 		rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result);
9073446Smrj 		goto OUT;
9083446Smrj 	}
9093446Smrj 
9103446Smrj 	if (pci_config_setup(pdip, &cfg_hdl) != DDI_SUCCESS) {
9113446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
9123446Smrj 		    "ppb_intr_ops: pci_config_setup() failed\n"));
9133446Smrj 		goto OUT;
9143446Smrj 	}
9153446Smrj 
9163446Smrj 	/*
9173446Smrj 	 * check for hypertransport msi mapping capability
9183446Smrj 	 */
9193446Smrj 	if (ppb_ht_msimap_check(cfg_hdl)) {
9203446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
9213446Smrj 		    "ppb_intr_ops: HT MSI mapping enabled\n"));
9223446Smrj 		rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result);
9233446Smrj 	}
9243446Smrj 
9253446Smrj 	/*
9263446Smrj 	 * if we add failure conditions after pci_config_setup, move this to
9273446Smrj 	 * OUT and use an extra flag to indicate the need to teardown cfg_hdl
9283446Smrj 	 */
9293446Smrj 	pci_config_teardown(&cfg_hdl);
9303446Smrj 
9313446Smrj OUT:
9323446Smrj 	DDI_INTR_NEXDBG((CE_CONT,
9333446Smrj 	    "ppb_intr_ops: rdip 0x%p, returns supported types: 0x%x\n",
9343446Smrj 	    (void *)rdip, *(int *)result));
9353446Smrj 	return (rv);
9363446Smrj }
9373446Smrj 
9383446Smrj static int
9393446Smrj ppb_open(dev_t *devp, int flags, int otyp, cred_t *credp)
9403446Smrj {
9413446Smrj 	return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp));
9423446Smrj }
9433446Smrj 
9443446Smrj static int
9453446Smrj ppb_close(dev_t dev, int flags, int otyp, cred_t *credp)
9463446Smrj {
9473446Smrj 	return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp));
9483446Smrj }
9493446Smrj 
9503446Smrj static int
9513446Smrj ppb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
9523446Smrj {
9533446Smrj 	return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, credp,
9543446Smrj 	    rvalp));
9553446Smrj }
9563446Smrj 
9573446Smrj static int
9583446Smrj ppb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
9593446Smrj 	int flags, char *name, caddr_t valuep, int *lengthp)
9603446Smrj {
9613446Smrj 	return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags,
9623446Smrj 	    name, valuep, lengthp));
9633446Smrj }
9643446Smrj 
9653446Smrj static int
9663446Smrj ppb_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
9673446Smrj {
9683446Smrj 	return (pcihp_info(dip, cmd, arg, result));
9693446Smrj }
9703446Smrj 
9716313Skrishnae void ppb_peekpoke_cb(dev_info_t *dip, ddi_fm_error_t *derr) {
9726313Skrishnae 	(void) pci_ereport_post(dip, derr, NULL);
9736313Skrishnae }
9746313Skrishnae 
9753446Smrj /*ARGSUSED*/
9763446Smrj static int
9773446Smrj ppb_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap,
9783446Smrj     ddi_iblock_cookie_t *ibc)
9793446Smrj {
9803446Smrj 	ppb_devstate_t  *ppb = ddi_get_soft_state(ppb_state,
9813446Smrj 	    ddi_get_instance(dip));
9823446Smrj 
9833446Smrj 	ASSERT(ibc != NULL);
9843446Smrj 	*ibc = ppb->ppb_fm_ibc;
9853446Smrj 
9863446Smrj 	return (ppb->ppb_fmcap);
9873446Smrj }
9883446Smrj 
9893446Smrj /*ARGSUSED*/
9903446Smrj static int
9913446Smrj ppb_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *no_used)
9923446Smrj {
9933446Smrj 	ppb_devstate_t  *ppb = ddi_get_soft_state(ppb_state,
9943446Smrj 	    ddi_get_instance(dip));
9953446Smrj 
9963446Smrj 	mutex_enter(&ppb->ppb_err_mutex);
9973446Smrj 	pci_ereport_post(dip, derr, NULL);
9983446Smrj 	mutex_exit(&ppb->ppb_err_mutex);
9993446Smrj 	return (derr->fme_status);
10003446Smrj }
1001