xref: /onnv-gate/usr/src/uts/intel/io/pci/pci_pci.c (revision 5295)
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 /*
22*5295Srandyf  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
233446Smrj  * Use is subject to license terms.
243446Smrj  */
253446Smrj 
263446Smrj #pragma ident	"%Z%%M%	%I%	%E% SMI"
273446Smrj 
283446Smrj /*
293446Smrj  * PCI to PCI bus bridge nexus driver
303446Smrj  */
313446Smrj 
323446Smrj #include <sys/conf.h>
333446Smrj #include <sys/kmem.h>
343446Smrj #include <sys/debug.h>
353446Smrj #include <sys/modctl.h>
363446Smrj #include <sys/autoconf.h>
373446Smrj #include <sys/ddi_impldefs.h>
383446Smrj #include <sys/pci.h>
393446Smrj #include <sys/ddi.h>
403446Smrj #include <sys/sunddi.h>
413446Smrj #include <sys/sunndi.h>
423446Smrj #include <sys/ddifm.h>
433446Smrj #include <sys/ndifm.h>
443446Smrj #include <sys/fm/protocol.h>
453446Smrj #include <sys/hotplug/pci/pcihp.h>
463446Smrj #include <sys/pci_intr_lib.h>
473446Smrj #include <sys/psm.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 /*
883446Smrj  * masks and values for the upper 16-bits of hypertransport cap headers
893446Smrj  */
903446Smrj #define	PCI_CAP_HT_MSIMAP_TYPE			0xA800
913446Smrj #define	PCI_CAP_HT_MSIMAP_TYPE_MASK		0xFF00
923446Smrj #define	PCI_CAP_HT_MSIMAP_ENABLE		0x0001
933446Smrj #define	PCI_CAP_HT_MSIMAP_ENABLE_MASK		0x0001
943446Smrj 
953446Smrj 
963446Smrj struct bus_ops ppb_bus_ops = {
973446Smrj 	BUSO_REV,
983446Smrj 	ppb_bus_map,
993446Smrj 	0,
1003446Smrj 	0,
1013446Smrj 	0,
1023446Smrj 	i_ddi_map_fault,
1033446Smrj 	ddi_dma_map,
1043446Smrj 	ddi_dma_allochdl,
1053446Smrj 	ddi_dma_freehdl,
1063446Smrj 	ddi_dma_bindhdl,
1073446Smrj 	ddi_dma_unbindhdl,
1083446Smrj 	ddi_dma_flush,
1093446Smrj 	ddi_dma_win,
1103446Smrj 	ddi_dma_mctl,
1113446Smrj 	ppb_ctlops,
1123446Smrj 	ddi_bus_prop_op,
1133446Smrj 	0,		/* (*bus_get_eventcookie)();	*/
1143446Smrj 	0,		/* (*bus_add_eventcall)();	*/
1153446Smrj 	0,		/* (*bus_remove_eventcall)();	*/
1163446Smrj 	0,		/* (*bus_post_event)();		*/
1173446Smrj 	0,		/* (*bus_intr_ctl)();		*/
1183446Smrj 	0,		/* (*bus_config)(); 		*/
1193446Smrj 	0,		/* (*bus_unconfig)(); 		*/
1203446Smrj 	ppb_fm_init,	/* (*bus_fm_init)(); 		*/
1213446Smrj 	NULL,		/* (*bus_fm_fini)(); 		*/
1223446Smrj 	NULL,		/* (*bus_fm_access_enter)(); 	*/
1233446Smrj 	NULL,		/* (*bus_fm_access_exit)(); 	*/
1243446Smrj 	NULL,		/* (*bus_power)(); 	*/
1253446Smrj 	ppb_intr_ops	/* (*bus_intr_op)(); 		*/
1263446Smrj };
1273446Smrj 
1283446Smrj /*
1293446Smrj  * The goal here is to leverage off of the pcihp.c source without making
1303446Smrj  * changes to it.  Call into it's cb_ops directly if needed.
1313446Smrj  */
1323446Smrj static int	ppb_open(dev_t *, int, int, cred_t *);
1333446Smrj static int	ppb_close(dev_t, int, int, cred_t *);
1343446Smrj static int	ppb_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1353446Smrj static int	ppb_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
1363446Smrj 		    caddr_t, int *);
1373446Smrj static int	ppb_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
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 */
1843446Smrj 	"PCI to PCI bridge nexus driver %I%",
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];
2193446Smrj } ppb_devstate_t;
2203446Smrj 
2213446Smrj 
2223446Smrj /*
2233446Smrj  * forward function declarations:
2243446Smrj  */
2253446Smrj static void	ppb_removechild(dev_info_t *);
2263446Smrj static int	ppb_initchild(dev_info_t *child);
2273446Smrj static void	ppb_save_config_regs(ppb_devstate_t *ppb_p);
2283446Smrj static void	ppb_restore_config_regs(ppb_devstate_t *ppb_p);
2293446Smrj static uint8_t	ppb_find_ht_cap(ddi_acc_handle_t cfg_hdl, uint16_t reg_mask,
2303446Smrj 		    uint16_t reg_val);
2313446Smrj static boolean_t	ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl);
2323446Smrj static int	ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd);
2333446Smrj 
2343446Smrj /*
2353446Smrj  * for <cmd> in ppb_ht_msimap_set
2363446Smrj  */
2373446Smrj #define	HT_MSIMAP_ENABLE	1
2383446Smrj #define	HT_MSIMAP_DISABLE	0
2393446Smrj 
2403446Smrj 
2413446Smrj int
2423446Smrj _init(void)
2433446Smrj {
2443446Smrj 	int e;
2453446Smrj 
2463446Smrj 	if ((e = ddi_soft_state_init(&ppb_state, sizeof (ppb_devstate_t),
2473446Smrj 	    1)) == 0 && (e = mod_install(&modlinkage)) != 0)
2483446Smrj 		ddi_soft_state_fini(&ppb_state);
2493446Smrj 	return (e);
2503446Smrj }
2513446Smrj 
2523446Smrj int
2533446Smrj _fini(void)
2543446Smrj {
2553446Smrj 	int e;
2563446Smrj 
2573446Smrj 	if ((e = mod_remove(&modlinkage)) == 0)
2583446Smrj 		ddi_soft_state_fini(&ppb_state);
2593446Smrj 	return (e);
2603446Smrj }
2613446Smrj 
2623446Smrj int
2633446Smrj _info(struct modinfo *modinfop)
2643446Smrj {
2653446Smrj 	return (mod_info(&modlinkage, modinfop));
2663446Smrj }
2673446Smrj 
2683446Smrj /*ARGSUSED*/
2693446Smrj static int
2703446Smrj ppb_probe(dev_info_t *devi)
2713446Smrj {
2723446Smrj 	return (DDI_PROBE_SUCCESS);
2733446Smrj }
2743446Smrj 
2753446Smrj /*ARGSUSED*/
2763446Smrj static int
2773446Smrj ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
2783446Smrj {
2793446Smrj 	int instance;
2803446Smrj 	ppb_devstate_t *ppb;
2813446Smrj 	ddi_acc_handle_t config_handle;
2823446Smrj 
2833446Smrj 	switch (cmd) {
2843446Smrj 	case DDI_ATTACH:
2853446Smrj 
2863446Smrj 		/*
2873446Smrj 		 * Make sure the "device_type" property exists.
2883446Smrj 		 */
2893446Smrj 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
2903446Smrj 		    "device_type", "pci");
2913446Smrj 
2923446Smrj 		/*
2933446Smrj 		 * Allocate and get soft state structure.
2943446Smrj 		 */
2953446Smrj 		instance = ddi_get_instance(devi);
2963446Smrj 		if (ddi_soft_state_zalloc(ppb_state, instance) != DDI_SUCCESS)
2973446Smrj 			return (DDI_FAILURE);
2983446Smrj 		ppb = ddi_get_soft_state(ppb_state, instance);
2993446Smrj 		ppb->dip = devi;
3003446Smrj 
3013446Smrj 		/*
3023446Smrj 		 * don't enable ereports if immediate child of npe
3033446Smrj 		 */
3043446Smrj 		if (strcmp(ddi_driver_name(ddi_get_parent(devi)), "npe") == 0)
3053446Smrj 			ppb->ppb_fmcap = DDI_FM_ERRCB_CAPABLE |
3063446Smrj 			    DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
3073446Smrj 		else
3083446Smrj 			ppb->ppb_fmcap = DDI_FM_EREPORT_CAPABLE |
3093446Smrj 			    DDI_FM_ERRCB_CAPABLE | DDI_FM_ACCCHK_CAPABLE |
3103446Smrj 			    DDI_FM_DMACHK_CAPABLE;
3113446Smrj 
3123446Smrj 		ddi_fm_init(devi, &ppb->ppb_fmcap, &ppb->ppb_fm_ibc);
3133446Smrj 		mutex_init(&ppb->ppb_err_mutex, NULL, MUTEX_DRIVER,
3143446Smrj 		    (void *)ppb->ppb_fm_ibc);
3153446Smrj 		mutex_init(&ppb->ppb_peek_poke_mutex, NULL, MUTEX_DRIVER,
3163446Smrj 		    (void *)ppb->ppb_fm_ibc);
3173446Smrj 
3183446Smrj 		if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
3193446Smrj 		    DDI_FM_EREPORT_CAPABLE))
3203446Smrj 			pci_ereport_setup(devi);
3213446Smrj 		if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
322*5295Srandyf 			ddi_fm_handler_register(devi, ppb_fm_callback, NULL);
3233446Smrj 
3243446Smrj 		if (pci_config_setup(devi, &config_handle) != DDI_SUCCESS) {
3253446Smrj 			if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
3263446Smrj 				ddi_fm_handler_unregister(devi);
3273446Smrj 			if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
3283446Smrj 			    DDI_FM_EREPORT_CAPABLE))
3293446Smrj 				pci_ereport_teardown(devi);
3303446Smrj 			ddi_fm_fini(devi);
3313446Smrj 			ddi_soft_state_free(ppb_state, instance);
3323446Smrj 			return (DDI_FAILURE);
3333446Smrj 		}
3343446Smrj 
3353446Smrj 		if (ppb_support_ht_msimap == 1)
3363446Smrj 			(void) ppb_ht_msimap_set(config_handle,
3373446Smrj 			    HT_MSIMAP_ENABLE);
3383446Smrj 		else if (ppb_support_ht_msimap == -1)
3393446Smrj 			(void) ppb_ht_msimap_set(config_handle,
3403446Smrj 			    HT_MSIMAP_DISABLE);
3413446Smrj 
3423446Smrj 		pci_config_teardown(&config_handle);
3433446Smrj 
3443446Smrj 		/*
3453446Smrj 		 * Initialize hotplug support on this bus. At minimum
3463446Smrj 		 * (for non hotplug bus) this would create ":devctl" minor
3473446Smrj 		 * node to support DEVCTL_DEVICE_* and DEVCTL_BUS_* ioctls
3483446Smrj 		 * to this bus.
3493446Smrj 		 */
3503446Smrj 		if (pcihp_init(devi) != DDI_SUCCESS)
351*5295Srandyf 			cmn_err(CE_WARN,
352*5295Srandyf 			    "pci: Failed to setup hotplug framework");
3533446Smrj 
3543446Smrj 		ddi_report_dev(devi);
3553446Smrj 		return (DDI_SUCCESS);
3563446Smrj 
3573446Smrj 	case DDI_RESUME:
3583446Smrj 
3593446Smrj 		/*
3603446Smrj 		 * Get the soft state structure for the bridge.
3613446Smrj 		 */
3623446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
3633446Smrj 		ppb_restore_config_regs(ppb);
3643446Smrj 		return (DDI_SUCCESS);
3653446Smrj 
3663446Smrj 	default:
3673446Smrj 		break;
3683446Smrj 	}
3693446Smrj 	return (DDI_FAILURE);
3703446Smrj }
3713446Smrj 
3723446Smrj /*ARGSUSED*/
3733446Smrj static int
3743446Smrj ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
3753446Smrj {
3763446Smrj 	ppb_devstate_t *ppb;
3773446Smrj 
3783446Smrj 	switch (cmd) {
3793446Smrj 	case DDI_DETACH:
3803446Smrj 		(void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type");
3813446Smrj 
3823446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
3833446Smrj 		if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
3843446Smrj 			ddi_fm_handler_unregister(devi);
3853446Smrj 		if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
3863446Smrj 		    DDI_FM_EREPORT_CAPABLE))
3873446Smrj 			pci_ereport_teardown(devi);
3883446Smrj 		mutex_destroy(&ppb->ppb_peek_poke_mutex);
3893446Smrj 		mutex_destroy(&ppb->ppb_err_mutex);
3903446Smrj 		ddi_fm_fini(devi);
3913446Smrj 
3923446Smrj 		/*
3933446Smrj 		 * And finally free the per-pci soft state.
3943446Smrj 		 */
3953446Smrj 		ddi_soft_state_free(ppb_state, ddi_get_instance(devi));
3963446Smrj 
3973446Smrj 		/*
3983446Smrj 		 * Uninitialize hotplug support on this bus.
3993446Smrj 		 */
4003446Smrj 		(void) pcihp_uninit(devi);
4013446Smrj 		return (DDI_SUCCESS);
4023446Smrj 
4033446Smrj 	case DDI_SUSPEND:
4043446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
4053446Smrj 		ppb_save_config_regs(ppb);
4063446Smrj 		return (DDI_SUCCESS);
4073446Smrj 
4083446Smrj 	default:
4093446Smrj 		break;
4103446Smrj 	}
4113446Smrj 	return (DDI_FAILURE);
4123446Smrj }
4133446Smrj 
4143446Smrj /*ARGSUSED*/
4153446Smrj static int
4163446Smrj ppb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
4173446Smrj 	off_t offset, off_t len, caddr_t *vaddrp)
4183446Smrj {
4193446Smrj 	dev_info_t *pdip;
4203446Smrj 
4213446Smrj 	pdip = (dev_info_t *)DEVI(dip)->devi_parent;
4223446Smrj 	return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip,
423*5295Srandyf 	    rdip, mp, offset, len, vaddrp));
4243446Smrj }
4253446Smrj 
4263446Smrj /*ARGSUSED*/
4273446Smrj static int
4283446Smrj ppb_ctlops(dev_info_t *dip, dev_info_t *rdip,
4293446Smrj 	ddi_ctl_enum_t ctlop, void *arg, void *result)
4303446Smrj {
4313446Smrj 	pci_regspec_t *drv_regp;
4323446Smrj 	int	reglen;
4333446Smrj 	int	rn;
4343446Smrj 	int	totreg;
4353446Smrj 	ppb_devstate_t *ppb;
436*5295Srandyf 	struct attachspec *asp;
4373446Smrj 
4383446Smrj 	switch (ctlop) {
4393446Smrj 	case DDI_CTLOPS_REPORTDEV:
4403446Smrj 		if (rdip == (dev_info_t *)0)
4413446Smrj 			return (DDI_FAILURE);
4423446Smrj 		cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n",
4433446Smrj 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
4443446Smrj 		    ddi_driver_name(rdip),
4453446Smrj 		    ddi_get_instance(rdip));
4463446Smrj 		return (DDI_SUCCESS);
4473446Smrj 
4483446Smrj 	case DDI_CTLOPS_INITCHILD:
4493446Smrj 		return (ppb_initchild((dev_info_t *)arg));
4503446Smrj 
4513446Smrj 	case DDI_CTLOPS_UNINITCHILD:
4523446Smrj 		ppb_removechild((dev_info_t *)arg);
4533446Smrj 		return (DDI_SUCCESS);
4543446Smrj 
4553446Smrj 	case DDI_CTLOPS_SIDDEV:
4563446Smrj 		return (DDI_SUCCESS);
4573446Smrj 
4583446Smrj 	case DDI_CTLOPS_REGSIZE:
4593446Smrj 	case DDI_CTLOPS_NREGS:
4603446Smrj 		if (rdip == (dev_info_t *)0)
4613446Smrj 			return (DDI_FAILURE);
4623446Smrj 		break;
4633446Smrj 
464*5295Srandyf 	/* X86 systems support PME wakeup from suspend */
465*5295Srandyf 	case DDI_CTLOPS_ATTACH:
466*5295Srandyf 		asp = (struct attachspec *)arg;
467*5295Srandyf 		if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE)
468*5295Srandyf 			if (pci_pre_resume(rdip) != DDI_SUCCESS)
469*5295Srandyf 				return (DDI_FAILURE);
470*5295Srandyf 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
471*5295Srandyf 
472*5295Srandyf 
473*5295Srandyf 	case DDI_CTLOPS_DETACH:
474*5295Srandyf 		asp = (struct attachspec *)arg;
475*5295Srandyf 		if (asp->cmd == DDI_SUSPEND && asp->when == DDI_POST)
476*5295Srandyf 			if (pci_post_suspend(rdip) != DDI_SUCCESS)
477*5295Srandyf 				return (DDI_FAILURE);
478*5295Srandyf 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
479*5295Srandyf 
4803446Smrj 	case DDI_CTLOPS_PEEK:
4813446Smrj 	case DDI_CTLOPS_POKE:
4823446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(dip));
4833446Smrj 		if (strcmp(ddi_driver_name(ddi_get_parent(dip)), "npe") != 0)
4843446Smrj 			return (ddi_ctlops(dip, rdip, ctlop, arg, result));
4853446Smrj 		return (pci_peekpoke_check(dip, rdip, ctlop, arg, result,
4863446Smrj 		    ddi_ctlops, &ppb->ppb_err_mutex,
4873446Smrj 		    &ppb->ppb_peek_poke_mutex));
4883446Smrj 
4893446Smrj 	default:
4903446Smrj 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
4913446Smrj 	}
4923446Smrj 
4933446Smrj 	*(int *)result = 0;
4943446Smrj 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip,
495*5295Srandyf 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg",
496*5295Srandyf 	    (caddr_t)&drv_regp, &reglen) != DDI_SUCCESS)
4973446Smrj 		return (DDI_FAILURE);
4983446Smrj 
4993446Smrj 	totreg = reglen / sizeof (pci_regspec_t);
5003446Smrj 	if (ctlop == DDI_CTLOPS_NREGS)
5013446Smrj 		*(int *)result = totreg;
5023446Smrj 	else if (ctlop == DDI_CTLOPS_REGSIZE) {
5033446Smrj 		rn = *(int *)arg;
5043446Smrj 		if (rn >= totreg) {
5053446Smrj 			kmem_free(drv_regp, reglen);
5063446Smrj 			return (DDI_FAILURE);
5073446Smrj 		}
5083446Smrj 		*(off_t *)result = drv_regp[rn].pci_size_low;
5093446Smrj 	}
5103446Smrj 
5113446Smrj 	kmem_free(drv_regp, reglen);
5123446Smrj 	return (DDI_SUCCESS);
5133446Smrj }
5143446Smrj 
5153446Smrj static int
5163446Smrj ppb_name_child(dev_info_t *child, char *name, int namelen)
5173446Smrj {
5183446Smrj 	pci_regspec_t *pci_rp;
5193446Smrj 	uint_t slot, func;
5203446Smrj 	char **unit_addr;
5213446Smrj 	uint_t n;
5223446Smrj 
5233446Smrj 	/*
5243446Smrj 	 * For .conf nodes, use unit-address property as name
5253446Smrj 	 */
5263446Smrj 	if (ndi_dev_is_persistent_node(child) == 0) {
5273446Smrj 		if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
5283446Smrj 		    DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) !=
5293446Smrj 		    DDI_PROP_SUCCESS) {
5303446Smrj 			cmn_err(CE_WARN,
5313446Smrj 			    "cannot find unit-address in %s.conf",
5323446Smrj 			    ddi_driver_name(child));
5333446Smrj 			return (DDI_FAILURE);
5343446Smrj 		}
5353446Smrj 		if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
5363446Smrj 			cmn_err(CE_WARN, "unit-address property in %s.conf"
5373446Smrj 			    " not well-formed", ddi_driver_name(child));
5383446Smrj 			ddi_prop_free(unit_addr);
5393446Smrj 			return (DDI_SUCCESS);
5403446Smrj 		}
5413446Smrj 		(void) snprintf(name, namelen, "%s", *unit_addr);
5423446Smrj 		ddi_prop_free(unit_addr);
5433446Smrj 		return (DDI_SUCCESS);
5443446Smrj 	}
5453446Smrj 
5463446Smrj 	/* get child "reg" property */
5473446Smrj 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
5483446Smrj 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) {
5493446Smrj 		return (DDI_FAILURE);
5503446Smrj 	}
5513446Smrj 
5523446Smrj 	/* copy the device identifications */
5533446Smrj 	slot = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
5543446Smrj 	func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
5553446Smrj 
5563446Smrj 	if (func != 0)
5573446Smrj 		(void) snprintf(name, namelen, "%x,%x", slot, func);
5583446Smrj 	else
5593446Smrj 		(void) snprintf(name, namelen, "%x", slot);
5603446Smrj 
5613446Smrj 	ddi_prop_free(pci_rp);
5623446Smrj 	return (DDI_SUCCESS);
5633446Smrj }
5643446Smrj 
5653446Smrj static int
5663446Smrj ppb_initchild(dev_info_t *child)
5673446Smrj {
5683446Smrj 	struct ddi_parent_private_data *pdptr;
5693446Smrj 	char name[MAXNAMELEN];
5703446Smrj 	ddi_acc_handle_t config_handle;
5713446Smrj 	ushort_t command_preserve, command;
5723446Smrj 
5733446Smrj 	if (ppb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS)
5743446Smrj 		return (DDI_FAILURE);
5753446Smrj 	ddi_set_name_addr(child, name);
5763446Smrj 
5773446Smrj 	/*
5783446Smrj 	 * Pseudo nodes indicate a prototype node with per-instance
5793446Smrj 	 * properties to be merged into the real h/w device node.
5803446Smrj 	 * The interpretation of the unit-address is DD[,F]
5813446Smrj 	 * where DD is the device id and F is the function.
5823446Smrj 	 */
5833446Smrj 	if (ndi_dev_is_persistent_node(child) == 0) {
5843446Smrj 		extern int pci_allow_pseudo_children;
5853446Smrj 
5863446Smrj 		ddi_set_parent_data(child, NULL);
5873446Smrj 
5883446Smrj 		/*
5893446Smrj 		 * Try to merge the properties from this prototype
5903446Smrj 		 * node into real h/w nodes.
5913446Smrj 		 */
5923446Smrj 		if (ndi_merge_node(child, ppb_name_child) == DDI_SUCCESS) {
5933446Smrj 			/*
5943446Smrj 			 * Merged ok - return failure to remove the node.
5953446Smrj 			 */
5963446Smrj 			ddi_set_name_addr(child, NULL);
5973446Smrj 			return (DDI_FAILURE);
5983446Smrj 		}
5993446Smrj 
6003446Smrj 		/* workaround for ddivs to run under PCI */
6013446Smrj 		if (pci_allow_pseudo_children)
6023446Smrj 			return (DDI_SUCCESS);
6033446Smrj 
6043446Smrj 		/*
6053446Smrj 		 * The child was not merged into a h/w node,
6063446Smrj 		 * but there's not much we can do with it other
6073446Smrj 		 * than return failure to cause the node to be removed.
6083446Smrj 		 */
6093446Smrj 		cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
6103446Smrj 		    ddi_driver_name(child), ddi_get_name_addr(child),
6113446Smrj 		    ddi_driver_name(child));
6123446Smrj 		ddi_set_name_addr(child, NULL);
6133446Smrj 		return (DDI_NOT_WELL_FORMED);
6143446Smrj 	}
6153446Smrj 
6163446Smrj 	/* transfer select properties from PROM to kernel */
617*5295Srandyf 	if (ddi_getprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS,
618*5295Srandyf 	    "interrupts", -1) != -1) {
6193446Smrj 		pdptr = kmem_zalloc((sizeof (struct ddi_parent_private_data) +
6203446Smrj 		    sizeof (struct intrspec)), KM_SLEEP);
6213446Smrj 		pdptr->par_intr = (struct intrspec *)(pdptr + 1);
6223446Smrj 		pdptr->par_nintr = 1;
6233446Smrj 		ddi_set_parent_data(child, pdptr);
6243446Smrj 	} else
6253446Smrj 		ddi_set_parent_data(child, NULL);
6263446Smrj 
6273446Smrj 	if (pci_config_setup(child, &config_handle) != DDI_SUCCESS)
6283446Smrj 		return (DDI_FAILURE);
6293446Smrj 
6303446Smrj 	/*
6313446Smrj 	 * Support for the "command-preserve" property.
6323446Smrj 	 */
6333446Smrj 	command_preserve = ddi_prop_get_int(DDI_DEV_T_ANY, child,
6343446Smrj 	    DDI_PROP_DONTPASS, "command-preserve", 0);
6353446Smrj 	command = pci_config_get16(config_handle, PCI_CONF_COMM);
6363446Smrj 	command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB);
6373446Smrj 	command |= (ppb_command_default & ~command_preserve);
6383446Smrj 	pci_config_put16(config_handle, PCI_CONF_COMM, command);
6393446Smrj 
6403446Smrj 	pci_config_teardown(&config_handle);
6413446Smrj 	return (DDI_SUCCESS);
6423446Smrj }
6433446Smrj 
6443446Smrj static void
6453446Smrj ppb_removechild(dev_info_t *dip)
6463446Smrj {
6473446Smrj 	struct ddi_parent_private_data *pdptr;
6483446Smrj 
6493446Smrj 	if ((pdptr = ddi_get_parent_data(dip)) != NULL) {
6503446Smrj 		kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec)));
6513446Smrj 		ddi_set_parent_data(dip, NULL);
6523446Smrj 	}
6533446Smrj 	ddi_set_name_addr(dip, NULL);
6543446Smrj 
6553446Smrj 	/*
6563446Smrj 	 * Strip the node to properly convert it back to prototype form
6573446Smrj 	 */
6583446Smrj 	ddi_remove_minor_node(dip, NULL);
6593446Smrj 
6603446Smrj 	impl_rem_dev_props(dip);
6613446Smrj }
6623446Smrj 
6633446Smrj /*
6643446Smrj  * ppb_save_config_regs
6653446Smrj  *
6663446Smrj  * This routine saves the state of the configuration registers of all
6673446Smrj  * the child nodes of each PBM.
6683446Smrj  *
6693446Smrj  * used by: ppb_detach() on suspends
6703446Smrj  *
6713446Smrj  * return value: none
6723446Smrj  */
6733446Smrj static void
6743446Smrj ppb_save_config_regs(ppb_devstate_t *ppb_p)
6753446Smrj {
6763446Smrj 	int i;
6773446Smrj 	dev_info_t *dip;
6783446Smrj 	ddi_acc_handle_t config_handle;
6793446Smrj 
6803446Smrj 	for (i = 0, dip = ddi_get_child(ppb_p->dip); dip != NULL;
681*5295Srandyf 	    i++, dip = ddi_get_next_sibling(dip)) {
6823446Smrj 
6833446Smrj 		if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
6843446Smrj 			cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
685*5295Srandyf 			    ddi_driver_name(ppb_p->dip),
686*5295Srandyf 			    ddi_get_instance(ppb_p->dip),
687*5295Srandyf 			    ddi_driver_name(dip),
688*5295Srandyf 			    ddi_get_instance(dip));
6893446Smrj 			continue;
6903446Smrj 		}
6913446Smrj 
6923446Smrj 		ppb_p->config_state[i].dip = dip;
6933446Smrj 		ppb_p->config_state[i].command =
694*5295Srandyf 		    pci_config_get16(config_handle, PCI_CONF_COMM);
6953446Smrj 		pci_config_teardown(&config_handle);
6963446Smrj 	}
6973446Smrj 	ppb_p->config_state_index = i;
6983446Smrj }
6993446Smrj 
7003446Smrj 
7013446Smrj /*
7023446Smrj  * ppb_restore_config_regs
7033446Smrj  *
7043446Smrj  * This routine restores the state of the configuration registers of all
7053446Smrj  * the child nodes of each PBM.
7063446Smrj  *
7073446Smrj  * used by: ppb_attach() on resume
7083446Smrj  *
7093446Smrj  * return value: none
7103446Smrj  */
7113446Smrj static void
7123446Smrj ppb_restore_config_regs(ppb_devstate_t *ppb_p)
7133446Smrj {
7143446Smrj 	int i;
7153446Smrj 	dev_info_t *dip;
7163446Smrj 	ddi_acc_handle_t config_handle;
7173446Smrj 
7183446Smrj 	for (i = 0; i < ppb_p->config_state_index; i++) {
7193446Smrj 		dip = ppb_p->config_state[i].dip;
7203446Smrj 		if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
7213446Smrj 			cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
722*5295Srandyf 			    ddi_driver_name(ppb_p->dip),
723*5295Srandyf 			    ddi_get_instance(ppb_p->dip),
724*5295Srandyf 			    ddi_driver_name(dip),
725*5295Srandyf 			    ddi_get_instance(dip));
7263446Smrj 			continue;
7273446Smrj 		}
7283446Smrj 		pci_config_put16(config_handle, PCI_CONF_COMM,
729*5295Srandyf 		    ppb_p->config_state[i].command);
7303446Smrj 		pci_config_teardown(&config_handle);
7313446Smrj 	}
7323446Smrj }
7333446Smrj 
7343446Smrj 
7353446Smrj /*
7363446Smrj  * returns the location of a hypertransport capability whose upper 16-bit
7373446Smrj  * register of the cap header matches <reg_val> after masking the register
7383446Smrj  * with <reg_mask>; if both <reg_mask> and <reg_val> are 0, it will return the
7393446Smrj  * first HT cap found
7403446Smrj  */
7413446Smrj static uint8_t
7423446Smrj ppb_find_ht_cap(ddi_acc_handle_t cfg_hdl, uint16_t reg_mask, uint16_t reg_val)
7433446Smrj {
7443446Smrj 	uint16_t status, reg;
7453446Smrj 	uint8_t ptr, id;
7463446Smrj 
7473446Smrj 	status = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
7483446Smrj 	if (status == 0xffff || !((status & PCI_STAT_CAP)))
7493446Smrj 		return (PCI_CAP_NEXT_PTR_NULL);
7503446Smrj 
7513446Smrj 	ptr = pci_config_get8(cfg_hdl, PCI_CONF_CAP_PTR);
7523446Smrj 	while (ptr != 0xFF &&
7533446Smrj 	    ptr != PCI_CAP_NEXT_PTR_NULL &&
7543446Smrj 	    ptr >= PCI_CAP_PTR_OFF) {
7553446Smrj 
7563446Smrj 		ptr &= PCI_CAP_PTR_MASK;
7573446Smrj 		id = pci_config_get8(cfg_hdl, ptr + PCI_CAP_ID);
7583446Smrj 
7593446Smrj 		if (id == PCI_CAP_ID_HT) {
7603446Smrj 			reg = pci_config_get16(cfg_hdl,
7613446Smrj 			    ptr + PCI_CAP_ID_REGS_OFF);
7623446Smrj 			if ((reg & reg_mask) == reg_val)
7633446Smrj 				return (ptr);
7643446Smrj 		}
7653446Smrj 		ptr = pci_config_get8(cfg_hdl, ptr + PCI_CAP_NEXT_PTR);
7663446Smrj 	}
7673446Smrj 
7683446Smrj 	return (PCI_CAP_NEXT_PTR_NULL);
7693446Smrj }
7703446Smrj 
7713446Smrj 
7723446Smrj static boolean_t
7733446Smrj ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl)
7743446Smrj {
7753446Smrj 	uint8_t ptr;
7763446Smrj 
7773446Smrj 	ptr = ppb_find_ht_cap(cfg_hdl,
7783446Smrj 	    PCI_CAP_HT_MSIMAP_TYPE_MASK | PCI_CAP_HT_MSIMAP_ENABLE_MASK,
7793446Smrj 	    PCI_CAP_HT_MSIMAP_TYPE | PCI_CAP_HT_MSIMAP_ENABLE);
7803446Smrj 
7813446Smrj 	if (ptr == PCI_CAP_NEXT_PTR_NULL)
7823446Smrj 		return (B_FALSE);
7833446Smrj 
7843446Smrj 	return (B_TRUE);
7853446Smrj }
7863446Smrj 
7873446Smrj 
7883446Smrj static int
7893446Smrj ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd)
7903446Smrj {
7913446Smrj 	uint8_t ptr;
7923446Smrj 	uint16_t reg;
7933446Smrj 
7943446Smrj 	ptr = ppb_find_ht_cap(cfg_hdl, PCI_CAP_HT_MSIMAP_TYPE_MASK,
7953446Smrj 	    PCI_CAP_HT_MSIMAP_TYPE);
7963446Smrj 	if (ptr == PCI_CAP_NEXT_PTR_NULL)
7973446Smrj 		return (0);
7983446Smrj 
7993446Smrj 	reg = pci_config_get16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF);
8003446Smrj 	switch (cmd) {
8013446Smrj 	case HT_MSIMAP_ENABLE:
8023446Smrj 		reg |= PCI_CAP_HT_MSIMAP_ENABLE;
8033446Smrj 		break;
8043446Smrj 	case HT_MSIMAP_DISABLE:
8053446Smrj 	default:
8063446Smrj 		reg &= ~(uint16_t)PCI_CAP_HT_MSIMAP_ENABLE;
8073446Smrj 	}
8083446Smrj 
8093446Smrj 	pci_config_put16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF, reg);
8103446Smrj 	return (1);
8113446Smrj }
8123446Smrj 
8133446Smrj 
8143446Smrj /*
8153446Smrj  * intercept certain interrupt services to handle special cases
8163446Smrj  */
8173446Smrj static int
8183446Smrj ppb_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
8193446Smrj     ddi_intr_handle_impl_t *hdlp, void *result)
8203446Smrj {
8213446Smrj 	ddi_acc_handle_t cfg_hdl;
8223446Smrj 	int rv = DDI_SUCCESS;
8233446Smrj 
8243446Smrj 	if (intr_op != DDI_INTROP_SUPPORTED_TYPES)
8253446Smrj 		return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result));
8263446Smrj 
8273446Smrj 	DDI_INTR_NEXDBG((CE_CONT,
8283446Smrj 	    "ppb_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
8293446Smrj 	    (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
8303446Smrj 
8313446Smrj 	/* Fixed interrupt is supported by default */
8323446Smrj 	*(int *)result = DDI_INTR_TYPE_FIXED;
8333446Smrj 
8343446Smrj 	if (ppb_support_msi == -1) {
8353446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
8363446Smrj 		    "ppb_intr_ops: MSI is not allowed\n"));
8373446Smrj 		goto OUT;
8383446Smrj 	}
8393446Smrj 
8403446Smrj 	if (ppb_support_msi == 1) {
8413446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
8423446Smrj 		    "ppb_intr_ops: MSI is always allowed\n"));
8433446Smrj 		rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result);
8443446Smrj 		goto OUT;
8453446Smrj 	}
8463446Smrj 
8473446Smrj 	if (pci_config_setup(pdip, &cfg_hdl) != DDI_SUCCESS) {
8483446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
8493446Smrj 		    "ppb_intr_ops: pci_config_setup() failed\n"));
8503446Smrj 		goto OUT;
8513446Smrj 	}
8523446Smrj 
8533446Smrj 	/*
8543446Smrj 	 * check for hypertransport msi mapping capability
8553446Smrj 	 */
8563446Smrj 	if (ppb_ht_msimap_check(cfg_hdl)) {
8573446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
8583446Smrj 		    "ppb_intr_ops: HT MSI mapping enabled\n"));
8593446Smrj 		rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result);
8603446Smrj 	}
8613446Smrj 
8623446Smrj 	/*
8633446Smrj 	 * if we add failure conditions after pci_config_setup, move this to
8643446Smrj 	 * OUT and use an extra flag to indicate the need to teardown cfg_hdl
8653446Smrj 	 */
8663446Smrj 	pci_config_teardown(&cfg_hdl);
8673446Smrj 
8683446Smrj OUT:
8693446Smrj 	DDI_INTR_NEXDBG((CE_CONT,
8703446Smrj 	    "ppb_intr_ops: rdip 0x%p, returns supported types: 0x%x\n",
8713446Smrj 	    (void *)rdip, *(int *)result));
8723446Smrj 	return (rv);
8733446Smrj }
8743446Smrj 
8753446Smrj static int
8763446Smrj ppb_open(dev_t *devp, int flags, int otyp, cred_t *credp)
8773446Smrj {
8783446Smrj 	return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp));
8793446Smrj }
8803446Smrj 
8813446Smrj static int
8823446Smrj ppb_close(dev_t dev, int flags, int otyp, cred_t *credp)
8833446Smrj {
8843446Smrj 	return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp));
8853446Smrj }
8863446Smrj 
8873446Smrj static int
8883446Smrj ppb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
8893446Smrj {
8903446Smrj 	return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, credp,
8913446Smrj 	    rvalp));
8923446Smrj }
8933446Smrj 
8943446Smrj static int
8953446Smrj ppb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
8963446Smrj 	int flags, char *name, caddr_t valuep, int *lengthp)
8973446Smrj {
8983446Smrj 	return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags,
8993446Smrj 	    name, valuep, lengthp));
9003446Smrj }
9013446Smrj 
9023446Smrj static int
9033446Smrj ppb_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
9043446Smrj {
9053446Smrj 	return (pcihp_info(dip, cmd, arg, result));
9063446Smrj }
9073446Smrj 
9083446Smrj /*ARGSUSED*/
9093446Smrj static int
9103446Smrj ppb_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap,
9113446Smrj     ddi_iblock_cookie_t *ibc)
9123446Smrj {
9133446Smrj 	ppb_devstate_t  *ppb = ddi_get_soft_state(ppb_state,
9143446Smrj 	    ddi_get_instance(dip));
9153446Smrj 
9163446Smrj 	ASSERT(ibc != NULL);
9173446Smrj 	*ibc = ppb->ppb_fm_ibc;
9183446Smrj 
9193446Smrj 	return (ppb->ppb_fmcap);
9203446Smrj }
9213446Smrj 
9223446Smrj /*ARGSUSED*/
9233446Smrj static int
9243446Smrj ppb_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *no_used)
9253446Smrj {
9263446Smrj 	ppb_devstate_t  *ppb = ddi_get_soft_state(ppb_state,
9273446Smrj 	    ddi_get_instance(dip));
9283446Smrj 
9293446Smrj 	mutex_enter(&ppb->ppb_err_mutex);
9303446Smrj 	pci_ereport_post(dip, derr, NULL);
9313446Smrj 	mutex_exit(&ppb->ppb_err_mutex);
9323446Smrj 	return (derr->fme_status);
9333446Smrj }
934