xref: /onnv-gate/usr/src/uts/intel/io/pci/pci_pci.c (revision 9970)
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 /*
229921SKrishna.Elango@Sun.COM  * Copyright 2009 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>
47*9970SJimmy.Vetayases@Sun.COM #include <sys/pci_cap.h>
483446Smrj 
493446Smrj /*
503446Smrj  * The variable controls the default setting of the command register
513446Smrj  * for pci devices.  See ppb_initchild() for details.
523446Smrj  */
533446Smrj static ushort_t ppb_command_default = PCI_COMM_ME | PCI_COMM_MAE | PCI_COMM_IO;
543446Smrj 
553446Smrj 
563446Smrj static int	ppb_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *,
573446Smrj 		    off_t, off_t, caddr_t *);
583446Smrj static int	ppb_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
593446Smrj 		    void *, void *);
603446Smrj static int	ppb_fm_init(dev_info_t *, dev_info_t *, int,
613446Smrj 		    ddi_iblock_cookie_t *);
623446Smrj static int	ppb_fm_callback(dev_info_t *, ddi_fm_error_t *, const void *);
633446Smrj static int	ppb_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t,
643446Smrj 		    ddi_intr_handle_impl_t *, void *);
653446Smrj 
663446Smrj /*
673446Smrj  * ppb_support_msi: Flag that controls MSI support across P2P Bridges.
683446Smrj  * By default, MSI is not supported except for special cases like HT
693446Smrj  * bridges/tunnels that have HT MSI mapping enabled.
703446Smrj  *
713446Smrj  * However, MSI support behavior can be patched on a system by changing
723446Smrj  * the value of this flag as shown below:-
733446Smrj  *	 0 = default value, MSI is allowed by this driver for special cases
743446Smrj  *	 1 = MSI supported without any checks for this driver
753446Smrj  *	-1 = MSI not supported at all
763446Smrj  */
773446Smrj int ppb_support_msi = 0;
783446Smrj 
793446Smrj /*
803446Smrj  * Controls the usage of the Hypertransport MSI mapping capability
813446Smrj  *	0 = default value, leave hardware function as it is
823446Smrj  *	1 = always enable HT MSI mapping
833446Smrj  *     -1 = always disable HT MSI mapping
843446Smrj  */
853446Smrj int ppb_support_ht_msimap = 0;
863446Smrj 
873446Smrj struct bus_ops ppb_bus_ops = {
883446Smrj 	BUSO_REV,
893446Smrj 	ppb_bus_map,
903446Smrj 	0,
913446Smrj 	0,
923446Smrj 	0,
933446Smrj 	i_ddi_map_fault,
943446Smrj 	ddi_dma_map,
953446Smrj 	ddi_dma_allochdl,
963446Smrj 	ddi_dma_freehdl,
973446Smrj 	ddi_dma_bindhdl,
983446Smrj 	ddi_dma_unbindhdl,
993446Smrj 	ddi_dma_flush,
1003446Smrj 	ddi_dma_win,
1013446Smrj 	ddi_dma_mctl,
1023446Smrj 	ppb_ctlops,
1033446Smrj 	ddi_bus_prop_op,
1043446Smrj 	0,		/* (*bus_get_eventcookie)();	*/
1053446Smrj 	0,		/* (*bus_add_eventcall)();	*/
1063446Smrj 	0,		/* (*bus_remove_eventcall)();	*/
1073446Smrj 	0,		/* (*bus_post_event)();		*/
1083446Smrj 	0,		/* (*bus_intr_ctl)();		*/
1093446Smrj 	0,		/* (*bus_config)(); 		*/
1103446Smrj 	0,		/* (*bus_unconfig)(); 		*/
1113446Smrj 	ppb_fm_init,	/* (*bus_fm_init)(); 		*/
1123446Smrj 	NULL,		/* (*bus_fm_fini)(); 		*/
1133446Smrj 	NULL,		/* (*bus_fm_access_enter)(); 	*/
1143446Smrj 	NULL,		/* (*bus_fm_access_exit)(); 	*/
1153446Smrj 	NULL,		/* (*bus_power)(); 	*/
1163446Smrj 	ppb_intr_ops	/* (*bus_intr_op)(); 		*/
1173446Smrj };
1183446Smrj 
1193446Smrj /*
1203446Smrj  * The goal here is to leverage off of the pcihp.c source without making
1213446Smrj  * changes to it.  Call into it's cb_ops directly if needed.
1223446Smrj  */
1233446Smrj static int	ppb_open(dev_t *, int, int, cred_t *);
1243446Smrj static int	ppb_close(dev_t, int, int, cred_t *);
1253446Smrj static int	ppb_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1263446Smrj static int	ppb_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
1273446Smrj 		    caddr_t, int *);
1283446Smrj static int	ppb_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
1296313Skrishnae static void	ppb_peekpoke_cb(dev_info_t *, ddi_fm_error_t *);
1303446Smrj 
1313446Smrj struct cb_ops ppb_cb_ops = {
1323446Smrj 	ppb_open,			/* open */
1333446Smrj 	ppb_close,			/* close */
1343446Smrj 	nodev,				/* strategy */
1353446Smrj 	nodev,				/* print */
1363446Smrj 	nodev,				/* dump */
1373446Smrj 	nodev,				/* read */
1383446Smrj 	nodev,				/* write */
1393446Smrj 	ppb_ioctl,			/* ioctl */
1403446Smrj 	nodev,				/* devmap */
1413446Smrj 	nodev,				/* mmap */
1423446Smrj 	nodev,				/* segmap */
1433446Smrj 	nochpoll,			/* poll */
1443446Smrj 	ppb_prop_op,			/* cb_prop_op */
1453446Smrj 	NULL,				/* streamtab */
1463446Smrj 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
1473446Smrj 	CB_REV,				/* rev */
1483446Smrj 	nodev,				/* int (*cb_aread)() */
1493446Smrj 	nodev				/* int (*cb_awrite)() */
1503446Smrj };
1513446Smrj 
1523446Smrj 
1533446Smrj static int ppb_probe(dev_info_t *);
1543446Smrj static int ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
1553446Smrj static int ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
1563446Smrj 
1573446Smrj struct dev_ops ppb_ops = {
1583446Smrj 	DEVO_REV,		/* devo_rev */
1593446Smrj 	0,			/* refcnt  */
1603446Smrj 	ppb_info,		/* info */
1613446Smrj 	nulldev,		/* identify */
1623446Smrj 	ppb_probe,		/* probe */
1633446Smrj 	ppb_attach,		/* attach */
1643446Smrj 	ppb_detach,		/* detach */
1653446Smrj 	nulldev,		/* reset */
1663446Smrj 	&ppb_cb_ops,		/* driver operations */
1677656SSherry.Moore@Sun.COM 	&ppb_bus_ops,		/* bus operations */
1687656SSherry.Moore@Sun.COM 	NULL,			/* power */
1697656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* quiesce */
1703446Smrj };
1713446Smrj 
1723446Smrj /*
1733446Smrj  * Module linkage information for the kernel.
1743446Smrj  */
1753446Smrj 
1763446Smrj static struct modldrv modldrv = {
1773446Smrj 	&mod_driverops, /* Type of module */
1787542SRichard.Bean@Sun.COM 	"PCI to PCI bridge nexus driver",
1793446Smrj 	&ppb_ops,	/* driver ops */
1803446Smrj };
1813446Smrj 
1823446Smrj static struct modlinkage modlinkage = {
1833446Smrj 	MODREV_1,
1843446Smrj 	(void *)&modldrv,
1853446Smrj 	NULL
1863446Smrj };
1873446Smrj 
1883446Smrj /*
1893446Smrj  * soft state pointer and structure template:
1903446Smrj  */
1913446Smrj static void *ppb_state;
1923446Smrj 
1933446Smrj typedef struct {
1943446Smrj 	dev_info_t *dip;
1953446Smrj 	int ppb_fmcap;
1963446Smrj 	ddi_iblock_cookie_t ppb_fm_ibc;
1973446Smrj 	kmutex_t ppb_peek_poke_mutex;
1983446Smrj 	kmutex_t ppb_err_mutex;
1993446Smrj 
2003446Smrj 	/*
2013446Smrj 	 * cpr support:
2023446Smrj 	 */
2033446Smrj 	uint_t config_state_index;
2043446Smrj 	struct {
2053446Smrj 		dev_info_t *dip;
2063446Smrj 		ushort_t command;
2073446Smrj 		uchar_t cache_line_size;
2083446Smrj 		uchar_t latency_timer;
2093446Smrj 		uchar_t header_type;
2103446Smrj 		uchar_t sec_latency_timer;
2113446Smrj 		ushort_t bridge_control;
2123446Smrj 	} config_state[PCI_MAX_CHILDREN];
2136313Skrishnae 
2149921SKrishna.Elango@Sun.COM 	uint16_t parent_bus;
2153446Smrj } ppb_devstate_t;
2163446Smrj 
2173446Smrj 
2183446Smrj /*
2193446Smrj  * forward function declarations:
2203446Smrj  */
2213446Smrj static void	ppb_removechild(dev_info_t *);
2223446Smrj static int	ppb_initchild(dev_info_t *child);
2233446Smrj static void	ppb_save_config_regs(ppb_devstate_t *ppb_p);
2243446Smrj static void	ppb_restore_config_regs(ppb_devstate_t *ppb_p);
2253446Smrj static boolean_t	ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl);
2263446Smrj static int	ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd);
2273446Smrj 
2283446Smrj /*
2293446Smrj  * for <cmd> in ppb_ht_msimap_set
2303446Smrj  */
2313446Smrj #define	HT_MSIMAP_ENABLE	1
2323446Smrj #define	HT_MSIMAP_DISABLE	0
2333446Smrj 
2343446Smrj 
2353446Smrj int
2363446Smrj _init(void)
2373446Smrj {
2383446Smrj 	int e;
2393446Smrj 
2403446Smrj 	if ((e = ddi_soft_state_init(&ppb_state, sizeof (ppb_devstate_t),
2413446Smrj 	    1)) == 0 && (e = mod_install(&modlinkage)) != 0)
2423446Smrj 		ddi_soft_state_fini(&ppb_state);
2433446Smrj 	return (e);
2443446Smrj }
2453446Smrj 
2463446Smrj int
2473446Smrj _fini(void)
2483446Smrj {
2493446Smrj 	int e;
2503446Smrj 
2513446Smrj 	if ((e = mod_remove(&modlinkage)) == 0)
2523446Smrj 		ddi_soft_state_fini(&ppb_state);
2533446Smrj 	return (e);
2543446Smrj }
2553446Smrj 
2563446Smrj int
2573446Smrj _info(struct modinfo *modinfop)
2583446Smrj {
2593446Smrj 	return (mod_info(&modlinkage, modinfop));
2603446Smrj }
2613446Smrj 
2623446Smrj /*ARGSUSED*/
2633446Smrj static int
2643446Smrj ppb_probe(dev_info_t *devi)
2653446Smrj {
2663446Smrj 	return (DDI_PROBE_SUCCESS);
2673446Smrj }
2683446Smrj 
2693446Smrj /*ARGSUSED*/
2703446Smrj static int
2713446Smrj ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
2723446Smrj {
2736313Skrishnae 	dev_info_t *root = ddi_root_node();
2743446Smrj 	int instance;
2753446Smrj 	ppb_devstate_t *ppb;
2766313Skrishnae 	dev_info_t *pdip;
2773446Smrj 	ddi_acc_handle_t config_handle;
2786313Skrishnae 	char *bus;
2793446Smrj 
2803446Smrj 	switch (cmd) {
2813446Smrj 	case DDI_ATTACH:
2823446Smrj 
2833446Smrj 		/*
2843446Smrj 		 * Make sure the "device_type" property exists.
2853446Smrj 		 */
2863446Smrj 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
2873446Smrj 		    "device_type", "pci");
2883446Smrj 
2893446Smrj 		/*
2903446Smrj 		 * Allocate and get soft state structure.
2913446Smrj 		 */
2923446Smrj 		instance = ddi_get_instance(devi);
2933446Smrj 		if (ddi_soft_state_zalloc(ppb_state, instance) != DDI_SUCCESS)
2943446Smrj 			return (DDI_FAILURE);
2953446Smrj 		ppb = ddi_get_soft_state(ppb_state, instance);
2963446Smrj 		ppb->dip = devi;
2973446Smrj 
2983446Smrj 		/*
2993446Smrj 		 * don't enable ereports if immediate child of npe
3003446Smrj 		 */
3013446Smrj 		if (strcmp(ddi_driver_name(ddi_get_parent(devi)), "npe") == 0)
3023446Smrj 			ppb->ppb_fmcap = DDI_FM_ERRCB_CAPABLE |
3033446Smrj 			    DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
3043446Smrj 		else
3053446Smrj 			ppb->ppb_fmcap = DDI_FM_EREPORT_CAPABLE |
3063446Smrj 			    DDI_FM_ERRCB_CAPABLE | DDI_FM_ACCCHK_CAPABLE |
3073446Smrj 			    DDI_FM_DMACHK_CAPABLE;
3083446Smrj 
3093446Smrj 		ddi_fm_init(devi, &ppb->ppb_fmcap, &ppb->ppb_fm_ibc);
3103446Smrj 		mutex_init(&ppb->ppb_err_mutex, NULL, MUTEX_DRIVER,
3113446Smrj 		    (void *)ppb->ppb_fm_ibc);
3123446Smrj 		mutex_init(&ppb->ppb_peek_poke_mutex, NULL, MUTEX_DRIVER,
3133446Smrj 		    (void *)ppb->ppb_fm_ibc);
3143446Smrj 
3153446Smrj 		if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
3163446Smrj 		    DDI_FM_EREPORT_CAPABLE))
3173446Smrj 			pci_ereport_setup(devi);
3183446Smrj 		if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
3195295Srandyf 			ddi_fm_handler_register(devi, ppb_fm_callback, NULL);
3203446Smrj 
3213446Smrj 		if (pci_config_setup(devi, &config_handle) != DDI_SUCCESS) {
3223446Smrj 			if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
3233446Smrj 				ddi_fm_handler_unregister(devi);
3243446Smrj 			if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
3253446Smrj 			    DDI_FM_EREPORT_CAPABLE))
3263446Smrj 				pci_ereport_teardown(devi);
3273446Smrj 			ddi_fm_fini(devi);
3283446Smrj 			ddi_soft_state_free(ppb_state, instance);
3293446Smrj 			return (DDI_FAILURE);
3303446Smrj 		}
3313446Smrj 
3329921SKrishna.Elango@Sun.COM 		ppb->parent_bus = PCIE_PCIECAP_DEV_TYPE_PCI_PSEUDO;
3336313Skrishnae 		for (pdip = ddi_get_parent(devi); pdip && (pdip != root) &&
3346313Skrishnae 		    (ppb->parent_bus != PCIE_PCIECAP_DEV_TYPE_PCIE_DEV);
3356313Skrishnae 		    pdip = ddi_get_parent(pdip)) {
3366313Skrishnae 			if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip,
3376313Skrishnae 			    DDI_PROP_DONTPASS, "device_type", &bus) !=
3386313Skrishnae 			    DDI_PROP_SUCCESS)
3396313Skrishnae 				break;
3406313Skrishnae 
3416313Skrishnae 			if (strcmp(bus, "pciex") == 0)
3426313Skrishnae 				ppb->parent_bus =
3436313Skrishnae 				    PCIE_PCIECAP_DEV_TYPE_PCIE_DEV;
3446313Skrishnae 
3456313Skrishnae 			ddi_prop_free(bus);
3466313Skrishnae 		}
3476313Skrishnae 
3483446Smrj 		if (ppb_support_ht_msimap == 1)
3493446Smrj 			(void) ppb_ht_msimap_set(config_handle,
3503446Smrj 			    HT_MSIMAP_ENABLE);
3513446Smrj 		else if (ppb_support_ht_msimap == -1)
3523446Smrj 			(void) ppb_ht_msimap_set(config_handle,
3533446Smrj 			    HT_MSIMAP_DISABLE);
3543446Smrj 
3553446Smrj 		pci_config_teardown(&config_handle);
3563446Smrj 
3573446Smrj 		/*
3583446Smrj 		 * Initialize hotplug support on this bus. At minimum
3593446Smrj 		 * (for non hotplug bus) this would create ":devctl" minor
3603446Smrj 		 * node to support DEVCTL_DEVICE_* and DEVCTL_BUS_* ioctls
3613446Smrj 		 * to this bus.
3623446Smrj 		 */
3633446Smrj 		if (pcihp_init(devi) != DDI_SUCCESS)
3645295Srandyf 			cmn_err(CE_WARN,
3655295Srandyf 			    "pci: Failed to setup hotplug framework");
3663446Smrj 
3673446Smrj 		ddi_report_dev(devi);
3683446Smrj 		return (DDI_SUCCESS);
3693446Smrj 
3703446Smrj 	case DDI_RESUME:
3713446Smrj 
3723446Smrj 		/*
3733446Smrj 		 * Get the soft state structure for the bridge.
3743446Smrj 		 */
3753446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
3763446Smrj 		ppb_restore_config_regs(ppb);
3773446Smrj 		return (DDI_SUCCESS);
3783446Smrj 
3793446Smrj 	default:
3803446Smrj 		break;
3813446Smrj 	}
3823446Smrj 	return (DDI_FAILURE);
3833446Smrj }
3843446Smrj 
3853446Smrj /*ARGSUSED*/
3863446Smrj static int
3873446Smrj ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
3883446Smrj {
3893446Smrj 	ppb_devstate_t *ppb;
3903446Smrj 
3913446Smrj 	switch (cmd) {
3923446Smrj 	case DDI_DETACH:
3933446Smrj 		(void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type");
3943446Smrj 
3953446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
3963446Smrj 		if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
3973446Smrj 			ddi_fm_handler_unregister(devi);
3983446Smrj 		if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
3993446Smrj 		    DDI_FM_EREPORT_CAPABLE))
4003446Smrj 			pci_ereport_teardown(devi);
4013446Smrj 		mutex_destroy(&ppb->ppb_peek_poke_mutex);
4023446Smrj 		mutex_destroy(&ppb->ppb_err_mutex);
4033446Smrj 		ddi_fm_fini(devi);
4043446Smrj 
4053446Smrj 		/*
4063446Smrj 		 * And finally free the per-pci soft state.
4073446Smrj 		 */
4083446Smrj 		ddi_soft_state_free(ppb_state, ddi_get_instance(devi));
4093446Smrj 
4103446Smrj 		/*
4113446Smrj 		 * Uninitialize hotplug support on this bus.
4123446Smrj 		 */
4133446Smrj 		(void) pcihp_uninit(devi);
4143446Smrj 		return (DDI_SUCCESS);
4153446Smrj 
4163446Smrj 	case DDI_SUSPEND:
4173446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
4183446Smrj 		ppb_save_config_regs(ppb);
4193446Smrj 		return (DDI_SUCCESS);
4203446Smrj 
4213446Smrj 	default:
4223446Smrj 		break;
4233446Smrj 	}
4243446Smrj 	return (DDI_FAILURE);
4253446Smrj }
4263446Smrj 
4273446Smrj /*ARGSUSED*/
4283446Smrj static int
4293446Smrj ppb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
4303446Smrj 	off_t offset, off_t len, caddr_t *vaddrp)
4313446Smrj {
4323446Smrj 	dev_info_t *pdip;
4333446Smrj 
4343446Smrj 	pdip = (dev_info_t *)DEVI(dip)->devi_parent;
4353446Smrj 	return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip,
4365295Srandyf 	    rdip, mp, offset, len, vaddrp));
4373446Smrj }
4383446Smrj 
4393446Smrj /*ARGSUSED*/
4403446Smrj static int
4413446Smrj ppb_ctlops(dev_info_t *dip, dev_info_t *rdip,
4423446Smrj 	ddi_ctl_enum_t ctlop, void *arg, void *result)
4433446Smrj {
4443446Smrj 	pci_regspec_t *drv_regp;
4453446Smrj 	int	reglen;
4463446Smrj 	int	rn;
4473446Smrj 	int	totreg;
4486313Skrishnae 	ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state,
4496313Skrishnae 	    ddi_get_instance(dip));
4506313Skrishnae 	struct detachspec *dsp;
4515295Srandyf 	struct attachspec *asp;
4523446Smrj 
4533446Smrj 	switch (ctlop) {
4543446Smrj 	case DDI_CTLOPS_REPORTDEV:
4553446Smrj 		if (rdip == (dev_info_t *)0)
4563446Smrj 			return (DDI_FAILURE);
4573446Smrj 		cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n",
4583446Smrj 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
4593446Smrj 		    ddi_driver_name(rdip),
4603446Smrj 		    ddi_get_instance(rdip));
4613446Smrj 		return (DDI_SUCCESS);
4623446Smrj 
4633446Smrj 	case DDI_CTLOPS_INITCHILD:
4643446Smrj 		return (ppb_initchild((dev_info_t *)arg));
4653446Smrj 
4663446Smrj 	case DDI_CTLOPS_UNINITCHILD:
4673446Smrj 		ppb_removechild((dev_info_t *)arg);
4683446Smrj 		return (DDI_SUCCESS);
4693446Smrj 
4703446Smrj 	case DDI_CTLOPS_SIDDEV:
4713446Smrj 		return (DDI_SUCCESS);
4723446Smrj 
4733446Smrj 	case DDI_CTLOPS_REGSIZE:
4743446Smrj 	case DDI_CTLOPS_NREGS:
4753446Smrj 		if (rdip == (dev_info_t *)0)
4763446Smrj 			return (DDI_FAILURE);
4773446Smrj 		break;
4783446Smrj 
4795295Srandyf 	/* X86 systems support PME wakeup from suspend */
4805295Srandyf 	case DDI_CTLOPS_ATTACH:
4816313Skrishnae 		if (!pcie_is_child(dip, rdip))
4826313Skrishnae 			return (DDI_SUCCESS);
4836313Skrishnae 
4845295Srandyf 		asp = (struct attachspec *)arg;
4856313Skrishnae 		if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) &&
4866313Skrishnae 		    (asp->when == DDI_POST) && (asp->result == DDI_SUCCESS))
4876313Skrishnae 			pf_init(rdip, (void *)ppb->ppb_fm_ibc, asp->cmd);
4886313Skrishnae 
4895295Srandyf 		if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE)
4905295Srandyf 			if (pci_pre_resume(rdip) != DDI_SUCCESS)
4915295Srandyf 				return (DDI_FAILURE);
4925295Srandyf 
4936313Skrishnae 		return (DDI_SUCCESS);
4945295Srandyf 
4955295Srandyf 	case DDI_CTLOPS_DETACH:
4966313Skrishnae 		if (!pcie_is_child(dip, rdip))
4976313Skrishnae 			return (DDI_SUCCESS);
4986313Skrishnae 
4996313Skrishnae 		dsp = (struct detachspec *)arg;
5006313Skrishnae 		if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) &&
5016313Skrishnae 		    (dsp->when == DDI_PRE))
5026313Skrishnae 			pf_fini(rdip, dsp->cmd);
5036313Skrishnae 
5046313Skrishnae 		if (dsp->cmd == DDI_SUSPEND && dsp->when == DDI_POST)
5055295Srandyf 			if (pci_post_suspend(rdip) != DDI_SUCCESS)
5065295Srandyf 				return (DDI_FAILURE);
5076313Skrishnae 
5086313Skrishnae 		return (DDI_SUCCESS);
5095295Srandyf 
5103446Smrj 	case DDI_CTLOPS_PEEK:
5113446Smrj 	case DDI_CTLOPS_POKE:
5123446Smrj 		if (strcmp(ddi_driver_name(ddi_get_parent(dip)), "npe") != 0)
5133446Smrj 			return (ddi_ctlops(dip, rdip, ctlop, arg, result));
5143446Smrj 		return (pci_peekpoke_check(dip, rdip, ctlop, arg, result,
5153446Smrj 		    ddi_ctlops, &ppb->ppb_err_mutex,
5166313Skrishnae 		    &ppb->ppb_peek_poke_mutex, ppb_peekpoke_cb));
5173446Smrj 
5183446Smrj 	default:
5193446Smrj 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
5203446Smrj 	}
5213446Smrj 
5223446Smrj 	*(int *)result = 0;
5233446Smrj 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip,
5245295Srandyf 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg",
5255295Srandyf 	    (caddr_t)&drv_regp, &reglen) != DDI_SUCCESS)
5263446Smrj 		return (DDI_FAILURE);
5273446Smrj 
5283446Smrj 	totreg = reglen / sizeof (pci_regspec_t);
5293446Smrj 	if (ctlop == DDI_CTLOPS_NREGS)
5303446Smrj 		*(int *)result = totreg;
5313446Smrj 	else if (ctlop == DDI_CTLOPS_REGSIZE) {
5323446Smrj 		rn = *(int *)arg;
5333446Smrj 		if (rn >= totreg) {
5343446Smrj 			kmem_free(drv_regp, reglen);
5353446Smrj 			return (DDI_FAILURE);
5363446Smrj 		}
5373446Smrj 		*(off_t *)result = drv_regp[rn].pci_size_low;
5383446Smrj 	}
5393446Smrj 
5403446Smrj 	kmem_free(drv_regp, reglen);
5413446Smrj 	return (DDI_SUCCESS);
5423446Smrj }
5433446Smrj 
5443446Smrj static int
5453446Smrj ppb_name_child(dev_info_t *child, char *name, int namelen)
5463446Smrj {
5473446Smrj 	pci_regspec_t *pci_rp;
5483446Smrj 	uint_t slot, func;
5493446Smrj 	char **unit_addr;
5503446Smrj 	uint_t n;
5513446Smrj 
5523446Smrj 	/*
5533446Smrj 	 * For .conf nodes, use unit-address property as name
5543446Smrj 	 */
5553446Smrj 	if (ndi_dev_is_persistent_node(child) == 0) {
5563446Smrj 		if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
5573446Smrj 		    DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) !=
5583446Smrj 		    DDI_PROP_SUCCESS) {
5593446Smrj 			cmn_err(CE_WARN,
5603446Smrj 			    "cannot find unit-address in %s.conf",
5613446Smrj 			    ddi_driver_name(child));
5623446Smrj 			return (DDI_FAILURE);
5633446Smrj 		}
5643446Smrj 		if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
5653446Smrj 			cmn_err(CE_WARN, "unit-address property in %s.conf"
5663446Smrj 			    " not well-formed", ddi_driver_name(child));
5673446Smrj 			ddi_prop_free(unit_addr);
5683446Smrj 			return (DDI_SUCCESS);
5693446Smrj 		}
5703446Smrj 		(void) snprintf(name, namelen, "%s", *unit_addr);
5713446Smrj 		ddi_prop_free(unit_addr);
5723446Smrj 		return (DDI_SUCCESS);
5733446Smrj 	}
5743446Smrj 
5753446Smrj 	/* get child "reg" property */
5763446Smrj 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
5773446Smrj 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) {
5783446Smrj 		return (DDI_FAILURE);
5793446Smrj 	}
5803446Smrj 
5813446Smrj 	/* copy the device identifications */
5823446Smrj 	slot = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
5833446Smrj 	func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
5843446Smrj 
5853446Smrj 	if (func != 0)
5863446Smrj 		(void) snprintf(name, namelen, "%x,%x", slot, func);
5873446Smrj 	else
5883446Smrj 		(void) snprintf(name, namelen, "%x", slot);
5893446Smrj 
5903446Smrj 	ddi_prop_free(pci_rp);
5913446Smrj 	return (DDI_SUCCESS);
5923446Smrj }
5933446Smrj 
5943446Smrj static int
5953446Smrj ppb_initchild(dev_info_t *child)
5963446Smrj {
5973446Smrj 	struct ddi_parent_private_data *pdptr;
5986313Skrishnae 	ppb_devstate_t *ppb;
5993446Smrj 	char name[MAXNAMELEN];
6003446Smrj 	ddi_acc_handle_t config_handle;
6013446Smrj 	ushort_t command_preserve, command;
6023446Smrj 
6036313Skrishnae 	ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
6046313Skrishnae 	    ddi_get_instance(ddi_get_parent(child)));
6056313Skrishnae 
6063446Smrj 	if (ppb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS)
6073446Smrj 		return (DDI_FAILURE);
6083446Smrj 	ddi_set_name_addr(child, name);
6093446Smrj 
6103446Smrj 	/*
6113446Smrj 	 * Pseudo nodes indicate a prototype node with per-instance
6123446Smrj 	 * properties to be merged into the real h/w device node.
6133446Smrj 	 * The interpretation of the unit-address is DD[,F]
6143446Smrj 	 * where DD is the device id and F is the function.
6153446Smrj 	 */
6163446Smrj 	if (ndi_dev_is_persistent_node(child) == 0) {
6173446Smrj 		extern int pci_allow_pseudo_children;
6183446Smrj 
6193446Smrj 		ddi_set_parent_data(child, NULL);
6203446Smrj 
6213446Smrj 		/*
6223446Smrj 		 * Try to merge the properties from this prototype
6233446Smrj 		 * node into real h/w nodes.
6243446Smrj 		 */
6253446Smrj 		if (ndi_merge_node(child, ppb_name_child) == DDI_SUCCESS) {
6263446Smrj 			/*
6273446Smrj 			 * Merged ok - return failure to remove the node.
6283446Smrj 			 */
6293446Smrj 			ddi_set_name_addr(child, NULL);
6303446Smrj 			return (DDI_FAILURE);
6313446Smrj 		}
6323446Smrj 
6333446Smrj 		/* workaround for ddivs to run under PCI */
6343446Smrj 		if (pci_allow_pseudo_children)
6353446Smrj 			return (DDI_SUCCESS);
6363446Smrj 
6373446Smrj 		/*
6383446Smrj 		 * The child was not merged into a h/w node,
6393446Smrj 		 * but there's not much we can do with it other
6403446Smrj 		 * than return failure to cause the node to be removed.
6413446Smrj 		 */
6423446Smrj 		cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
6433446Smrj 		    ddi_driver_name(child), ddi_get_name_addr(child),
6443446Smrj 		    ddi_driver_name(child));
6453446Smrj 		ddi_set_name_addr(child, NULL);
6463446Smrj 		return (DDI_NOT_WELL_FORMED);
6473446Smrj 	}
6483446Smrj 
6496313Skrishnae 	ddi_set_parent_data(child, NULL);
6506313Skrishnae 
6516313Skrishnae 	/*
6526313Skrishnae 	 * PCIe FMA specific
6536313Skrishnae 	 *
6546313Skrishnae 	 * Note: parent_data for parent is created only if this is PCI-E
6556313Skrishnae 	 * platform, for which, SG take a different route to handle device
6566313Skrishnae 	 * errors.
6576313Skrishnae 	 */
6586313Skrishnae 	if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
6596313Skrishnae 		if (pcie_init_bus(child) == NULL)
6606313Skrishnae 			return (DDI_FAILURE);
6616313Skrishnae 	}
6626313Skrishnae 
6633446Smrj 	/* transfer select properties from PROM to kernel */
6645295Srandyf 	if (ddi_getprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS,
6655295Srandyf 	    "interrupts", -1) != -1) {
6663446Smrj 		pdptr = kmem_zalloc((sizeof (struct ddi_parent_private_data) +
6673446Smrj 		    sizeof (struct intrspec)), KM_SLEEP);
6683446Smrj 		pdptr->par_intr = (struct intrspec *)(pdptr + 1);
6693446Smrj 		pdptr->par_nintr = 1;
6703446Smrj 		ddi_set_parent_data(child, pdptr);
6713446Smrj 	} else
6723446Smrj 		ddi_set_parent_data(child, NULL);
6733446Smrj 
6743446Smrj 	if (pci_config_setup(child, &config_handle) != DDI_SUCCESS)
6753446Smrj 		return (DDI_FAILURE);
6763446Smrj 
6773446Smrj 	/*
6783446Smrj 	 * Support for the "command-preserve" property.
6793446Smrj 	 */
6803446Smrj 	command_preserve = ddi_prop_get_int(DDI_DEV_T_ANY, child,
6813446Smrj 	    DDI_PROP_DONTPASS, "command-preserve", 0);
6823446Smrj 	command = pci_config_get16(config_handle, PCI_CONF_COMM);
6833446Smrj 	command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB);
6843446Smrj 	command |= (ppb_command_default & ~command_preserve);
6853446Smrj 	pci_config_put16(config_handle, PCI_CONF_COMM, command);
6863446Smrj 
6873446Smrj 	pci_config_teardown(&config_handle);
6883446Smrj 	return (DDI_SUCCESS);
6893446Smrj }
6903446Smrj 
6913446Smrj static void
6923446Smrj ppb_removechild(dev_info_t *dip)
6933446Smrj {
6943446Smrj 	struct ddi_parent_private_data *pdptr;
6956313Skrishnae 	ppb_devstate_t *ppb;
6963446Smrj 
6976313Skrishnae 	ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
6986313Skrishnae 	    ddi_get_instance(ddi_get_parent(dip)));
6996313Skrishnae 
7006313Skrishnae 	if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
7016313Skrishnae 		pcie_fini_bus(dip);
7026313Skrishnae 	else if ((pdptr = ddi_get_parent_data(dip)) != NULL) {
7033446Smrj 		kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec)));
7043446Smrj 		ddi_set_parent_data(dip, NULL);
7053446Smrj 	}
7063446Smrj 	ddi_set_name_addr(dip, NULL);
7073446Smrj 
7083446Smrj 	/*
7093446Smrj 	 * Strip the node to properly convert it back to prototype form
7103446Smrj 	 */
7113446Smrj 	ddi_remove_minor_node(dip, NULL);
7123446Smrj 
7133446Smrj 	impl_rem_dev_props(dip);
7143446Smrj }
7153446Smrj 
7163446Smrj /*
7173446Smrj  * ppb_save_config_regs
7183446Smrj  *
7193446Smrj  * This routine saves the state of the configuration registers of all
7203446Smrj  * the child nodes of each PBM.
7213446Smrj  *
7223446Smrj  * used by: ppb_detach() on suspends
7233446Smrj  *
7243446Smrj  * return value: none
7253446Smrj  */
7263446Smrj static void
7273446Smrj ppb_save_config_regs(ppb_devstate_t *ppb_p)
7283446Smrj {
7293446Smrj 	int i;
7303446Smrj 	dev_info_t *dip;
7313446Smrj 	ddi_acc_handle_t config_handle;
7323446Smrj 
7333446Smrj 	for (i = 0, dip = ddi_get_child(ppb_p->dip); dip != NULL;
7345295Srandyf 	    i++, dip = ddi_get_next_sibling(dip)) {
7353446Smrj 
7363446Smrj 		if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
7373446Smrj 			cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
7385295Srandyf 			    ddi_driver_name(ppb_p->dip),
7395295Srandyf 			    ddi_get_instance(ppb_p->dip),
7405295Srandyf 			    ddi_driver_name(dip),
7415295Srandyf 			    ddi_get_instance(dip));
7423446Smrj 			continue;
7433446Smrj 		}
7443446Smrj 
7453446Smrj 		ppb_p->config_state[i].dip = dip;
7463446Smrj 		ppb_p->config_state[i].command =
7475295Srandyf 		    pci_config_get16(config_handle, PCI_CONF_COMM);
7483446Smrj 		pci_config_teardown(&config_handle);
7493446Smrj 	}
7503446Smrj 	ppb_p->config_state_index = i;
7513446Smrj }
7523446Smrj 
7533446Smrj 
7543446Smrj /*
7553446Smrj  * ppb_restore_config_regs
7563446Smrj  *
7573446Smrj  * This routine restores the state of the configuration registers of all
7583446Smrj  * the child nodes of each PBM.
7593446Smrj  *
7603446Smrj  * used by: ppb_attach() on resume
7613446Smrj  *
7623446Smrj  * return value: none
7633446Smrj  */
7643446Smrj static void
7653446Smrj ppb_restore_config_regs(ppb_devstate_t *ppb_p)
7663446Smrj {
7673446Smrj 	int i;
7683446Smrj 	dev_info_t *dip;
7693446Smrj 	ddi_acc_handle_t config_handle;
7703446Smrj 
7713446Smrj 	for (i = 0; i < ppb_p->config_state_index; i++) {
7723446Smrj 		dip = ppb_p->config_state[i].dip;
7733446Smrj 		if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
7743446Smrj 			cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
7755295Srandyf 			    ddi_driver_name(ppb_p->dip),
7765295Srandyf 			    ddi_get_instance(ppb_p->dip),
7775295Srandyf 			    ddi_driver_name(dip),
7785295Srandyf 			    ddi_get_instance(dip));
7793446Smrj 			continue;
7803446Smrj 		}
7813446Smrj 		pci_config_put16(config_handle, PCI_CONF_COMM,
7825295Srandyf 		    ppb_p->config_state[i].command);
7833446Smrj 		pci_config_teardown(&config_handle);
7843446Smrj 	}
7853446Smrj }
7863446Smrj 
7873446Smrj 
7883446Smrj static boolean_t
7893446Smrj ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl)
7903446Smrj {
791*9970SJimmy.Vetayases@Sun.COM 	uint16_t ptr;
7923446Smrj 
793*9970SJimmy.Vetayases@Sun.COM 	if (pci_htcap_locate(cfg_hdl,
794*9970SJimmy.Vetayases@Sun.COM 	    PCI_HTCAP_TYPE_MASK | PCI_HTCAP_MSIMAP_ENABLE_MASK,
795*9970SJimmy.Vetayases@Sun.COM 	    PCI_HTCAP_MSIMAP_TYPE | PCI_HTCAP_MSIMAP_ENABLE, &ptr) !=
796*9970SJimmy.Vetayases@Sun.COM 	    DDI_SUCCESS)
7973446Smrj 		return (B_FALSE);
7983446Smrj 
7993446Smrj 	return (B_TRUE);
8003446Smrj }
8013446Smrj 
8023446Smrj 
8033446Smrj static int
8043446Smrj ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd)
8053446Smrj {
806*9970SJimmy.Vetayases@Sun.COM 	uint16_t ptr;
8073446Smrj 	uint16_t reg;
8083446Smrj 
809*9970SJimmy.Vetayases@Sun.COM 	if (pci_htcap_locate(cfg_hdl, PCI_HTCAP_TYPE_MASK,
810*9970SJimmy.Vetayases@Sun.COM 	    PCI_HTCAP_MSIMAP_TYPE, &ptr) != DDI_SUCCESS)
8113446Smrj 		return (0);
8123446Smrj 
8133446Smrj 	reg = pci_config_get16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF);
8143446Smrj 	switch (cmd) {
8153446Smrj 	case HT_MSIMAP_ENABLE:
816*9970SJimmy.Vetayases@Sun.COM 		reg |= PCI_HTCAP_MSIMAP_ENABLE;
8173446Smrj 		break;
8183446Smrj 	case HT_MSIMAP_DISABLE:
8193446Smrj 	default:
820*9970SJimmy.Vetayases@Sun.COM 		reg &= ~(uint16_t)PCI_HTCAP_MSIMAP_ENABLE;
8213446Smrj 	}
8223446Smrj 
8233446Smrj 	pci_config_put16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF, reg);
8243446Smrj 	return (1);
8253446Smrj }
8263446Smrj 
8273446Smrj 
8283446Smrj /*
8293446Smrj  * intercept certain interrupt services to handle special cases
8303446Smrj  */
8313446Smrj static int
8323446Smrj ppb_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
8333446Smrj     ddi_intr_handle_impl_t *hdlp, void *result)
8343446Smrj {
8353446Smrj 	ddi_acc_handle_t cfg_hdl;
8363446Smrj 	int rv = DDI_SUCCESS;
8373446Smrj 
8383446Smrj 	if (intr_op != DDI_INTROP_SUPPORTED_TYPES)
8393446Smrj 		return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result));
8403446Smrj 
8413446Smrj 	DDI_INTR_NEXDBG((CE_CONT,
8423446Smrj 	    "ppb_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
8433446Smrj 	    (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
8443446Smrj 
8453446Smrj 	/* Fixed interrupt is supported by default */
8463446Smrj 	*(int *)result = DDI_INTR_TYPE_FIXED;
8473446Smrj 
8483446Smrj 	if (ppb_support_msi == -1) {
8493446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
8503446Smrj 		    "ppb_intr_ops: MSI is not allowed\n"));
8513446Smrj 		goto OUT;
8523446Smrj 	}
8533446Smrj 
8543446Smrj 	if (ppb_support_msi == 1) {
8553446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
8563446Smrj 		    "ppb_intr_ops: MSI is always allowed\n"));
8573446Smrj 		rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result);
8583446Smrj 		goto OUT;
8593446Smrj 	}
8603446Smrj 
8613446Smrj 	if (pci_config_setup(pdip, &cfg_hdl) != DDI_SUCCESS) {
8623446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
8633446Smrj 		    "ppb_intr_ops: pci_config_setup() failed\n"));
8643446Smrj 		goto OUT;
8653446Smrj 	}
8663446Smrj 
8673446Smrj 	/*
8683446Smrj 	 * check for hypertransport msi mapping capability
8693446Smrj 	 */
8703446Smrj 	if (ppb_ht_msimap_check(cfg_hdl)) {
8713446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
8723446Smrj 		    "ppb_intr_ops: HT MSI mapping enabled\n"));
8733446Smrj 		rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result);
8743446Smrj 	}
8753446Smrj 
8763446Smrj 	/*
8773446Smrj 	 * if we add failure conditions after pci_config_setup, move this to
8783446Smrj 	 * OUT and use an extra flag to indicate the need to teardown cfg_hdl
8793446Smrj 	 */
8803446Smrj 	pci_config_teardown(&cfg_hdl);
8813446Smrj 
8823446Smrj OUT:
8833446Smrj 	DDI_INTR_NEXDBG((CE_CONT,
8843446Smrj 	    "ppb_intr_ops: rdip 0x%p, returns supported types: 0x%x\n",
8853446Smrj 	    (void *)rdip, *(int *)result));
8863446Smrj 	return (rv);
8873446Smrj }
8883446Smrj 
8893446Smrj static int
8903446Smrj ppb_open(dev_t *devp, int flags, int otyp, cred_t *credp)
8913446Smrj {
8923446Smrj 	return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp));
8933446Smrj }
8943446Smrj 
8953446Smrj static int
8963446Smrj ppb_close(dev_t dev, int flags, int otyp, cred_t *credp)
8973446Smrj {
8983446Smrj 	return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp));
8993446Smrj }
9003446Smrj 
9013446Smrj static int
9023446Smrj ppb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
9033446Smrj {
9043446Smrj 	return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, credp,
9053446Smrj 	    rvalp));
9063446Smrj }
9073446Smrj 
9083446Smrj static int
9093446Smrj ppb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
9103446Smrj 	int flags, char *name, caddr_t valuep, int *lengthp)
9113446Smrj {
9123446Smrj 	return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags,
9133446Smrj 	    name, valuep, lengthp));
9143446Smrj }
9153446Smrj 
9163446Smrj static int
9173446Smrj ppb_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
9183446Smrj {
9193446Smrj 	return (pcihp_info(dip, cmd, arg, result));
9203446Smrj }
9213446Smrj 
9226313Skrishnae void ppb_peekpoke_cb(dev_info_t *dip, ddi_fm_error_t *derr) {
9236313Skrishnae 	(void) pci_ereport_post(dip, derr, NULL);
9246313Skrishnae }
9256313Skrishnae 
9263446Smrj /*ARGSUSED*/
9273446Smrj static int
9283446Smrj ppb_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap,
9293446Smrj     ddi_iblock_cookie_t *ibc)
9303446Smrj {
9313446Smrj 	ppb_devstate_t  *ppb = ddi_get_soft_state(ppb_state,
9323446Smrj 	    ddi_get_instance(dip));
9333446Smrj 
9343446Smrj 	ASSERT(ibc != NULL);
9353446Smrj 	*ibc = ppb->ppb_fm_ibc;
9363446Smrj 
9373446Smrj 	return (ppb->ppb_fmcap);
9383446Smrj }
9393446Smrj 
9403446Smrj /*ARGSUSED*/
9413446Smrj static int
9423446Smrj ppb_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *no_used)
9433446Smrj {
9443446Smrj 	ppb_devstate_t  *ppb = ddi_get_soft_state(ppb_state,
9453446Smrj 	    ddi_get_instance(dip));
9463446Smrj 
9473446Smrj 	mutex_enter(&ppb->ppb_err_mutex);
9483446Smrj 	pci_ereport_post(dip, derr, NULL);
9493446Smrj 	mutex_exit(&ppb->ppb_err_mutex);
9503446Smrj 	return (derr->fme_status);
9513446Smrj }
952