xref: /onnv-gate/usr/src/uts/intel/io/pci/pci_pci.c (revision 10923)
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>
37*10923SEvan.Yan@Sun.COM #include <sys/pci_impl.h>
386313Skrishnae #include <sys/pcie_impl.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>
45*10923SEvan.Yan@Sun.COM #include <sys/hotplug/pci/pcie_hp.h>
463446Smrj #include <sys/hotplug/pci/pcihp.h>
473446Smrj #include <sys/pci_intr_lib.h>
483446Smrj #include <sys/psm.h>
499970SJimmy.Vetayases@Sun.COM #include <sys/pci_cap.h>
503446Smrj 
513446Smrj /*
523446Smrj  * The variable controls the default setting of the command register
533446Smrj  * for pci devices.  See ppb_initchild() for details.
543446Smrj  */
553446Smrj static ushort_t ppb_command_default = PCI_COMM_ME | PCI_COMM_MAE | PCI_COMM_IO;
563446Smrj 
573446Smrj 
583446Smrj static int	ppb_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *,
593446Smrj 		    off_t, off_t, caddr_t *);
603446Smrj static int	ppb_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
613446Smrj 		    void *, void *);
623446Smrj static int	ppb_fm_init(dev_info_t *, dev_info_t *, int,
633446Smrj 		    ddi_iblock_cookie_t *);
643446Smrj static int	ppb_fm_callback(dev_info_t *, ddi_fm_error_t *, const void *);
653446Smrj static int	ppb_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t,
663446Smrj 		    ddi_intr_handle_impl_t *, void *);
673446Smrj 
683446Smrj /*
693446Smrj  * ppb_support_msi: Flag that controls MSI support across P2P Bridges.
703446Smrj  * By default, MSI is not supported except for special cases like HT
713446Smrj  * bridges/tunnels that have HT MSI mapping enabled.
723446Smrj  *
733446Smrj  * However, MSI support behavior can be patched on a system by changing
743446Smrj  * the value of this flag as shown below:-
753446Smrj  *	 0 = default value, MSI is allowed by this driver for special cases
763446Smrj  *	 1 = MSI supported without any checks for this driver
773446Smrj  *	-1 = MSI not supported at all
783446Smrj  */
793446Smrj int ppb_support_msi = 0;
803446Smrj 
813446Smrj /*
823446Smrj  * Controls the usage of the Hypertransport MSI mapping capability
833446Smrj  *	0 = default value, leave hardware function as it is
843446Smrj  *	1 = always enable HT MSI mapping
853446Smrj  *     -1 = always disable HT MSI mapping
863446Smrj  */
873446Smrj int ppb_support_ht_msimap = 0;
883446Smrj 
893446Smrj struct bus_ops ppb_bus_ops = {
903446Smrj 	BUSO_REV,
913446Smrj 	ppb_bus_map,
923446Smrj 	0,
933446Smrj 	0,
943446Smrj 	0,
953446Smrj 	i_ddi_map_fault,
963446Smrj 	ddi_dma_map,
973446Smrj 	ddi_dma_allochdl,
983446Smrj 	ddi_dma_freehdl,
993446Smrj 	ddi_dma_bindhdl,
1003446Smrj 	ddi_dma_unbindhdl,
1013446Smrj 	ddi_dma_flush,
1023446Smrj 	ddi_dma_win,
1033446Smrj 	ddi_dma_mctl,
1043446Smrj 	ppb_ctlops,
1053446Smrj 	ddi_bus_prop_op,
106*10923SEvan.Yan@Sun.COM 	0,			/* (*bus_get_eventcookie)();	*/
107*10923SEvan.Yan@Sun.COM 	0,			/* (*bus_add_eventcall)();	*/
108*10923SEvan.Yan@Sun.COM 	0,			/* (*bus_remove_eventcall)();	*/
109*10923SEvan.Yan@Sun.COM 	0,			/* (*bus_post_event)();		*/
110*10923SEvan.Yan@Sun.COM 	0,			/* (*bus_intr_ctl)();		*/
111*10923SEvan.Yan@Sun.COM 	0,			/* (*bus_config)(); 		*/
112*10923SEvan.Yan@Sun.COM 	0,			/* (*bus_unconfig)(); 		*/
113*10923SEvan.Yan@Sun.COM 	ppb_fm_init,		/* (*bus_fm_init)(); 		*/
114*10923SEvan.Yan@Sun.COM 	NULL,			/* (*bus_fm_fini)(); 		*/
115*10923SEvan.Yan@Sun.COM 	NULL,			/* (*bus_fm_access_enter)(); 	*/
116*10923SEvan.Yan@Sun.COM 	NULL,			/* (*bus_fm_access_exit)(); 	*/
117*10923SEvan.Yan@Sun.COM 	NULL,			/* (*bus_power)(); 	*/
118*10923SEvan.Yan@Sun.COM 	ppb_intr_ops,		/* (*bus_intr_op)(); 		*/
119*10923SEvan.Yan@Sun.COM 	pcie_hp_common_ops	/* (*bus_hp_op)(); 		*/
1203446Smrj };
1213446Smrj 
1223446Smrj /*
1233446Smrj  * The goal here is to leverage off of the pcihp.c source without making
1243446Smrj  * changes to it.  Call into it's cb_ops directly if needed.
1253446Smrj  */
1263446Smrj static int	ppb_open(dev_t *, int, int, cred_t *);
1273446Smrj static int	ppb_close(dev_t, int, int, cred_t *);
1283446Smrj static int	ppb_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1293446Smrj static int	ppb_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
1303446Smrj 		    caddr_t, int *);
1313446Smrj static int	ppb_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
1326313Skrishnae static void	ppb_peekpoke_cb(dev_info_t *, ddi_fm_error_t *);
1333446Smrj 
1343446Smrj struct cb_ops ppb_cb_ops = {
1353446Smrj 	ppb_open,			/* open */
1363446Smrj 	ppb_close,			/* close */
1373446Smrj 	nodev,				/* strategy */
1383446Smrj 	nodev,				/* print */
1393446Smrj 	nodev,				/* dump */
1403446Smrj 	nodev,				/* read */
1413446Smrj 	nodev,				/* write */
1423446Smrj 	ppb_ioctl,			/* ioctl */
1433446Smrj 	nodev,				/* devmap */
1443446Smrj 	nodev,				/* mmap */
1453446Smrj 	nodev,				/* segmap */
1463446Smrj 	nochpoll,			/* poll */
1473446Smrj 	ppb_prop_op,			/* cb_prop_op */
1483446Smrj 	NULL,				/* streamtab */
1493446Smrj 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
1503446Smrj 	CB_REV,				/* rev */
1513446Smrj 	nodev,				/* int (*cb_aread)() */
1523446Smrj 	nodev				/* int (*cb_awrite)() */
1533446Smrj };
1543446Smrj 
1553446Smrj 
1563446Smrj static int ppb_probe(dev_info_t *);
1573446Smrj static int ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
1583446Smrj static int ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
1593446Smrj 
1603446Smrj struct dev_ops ppb_ops = {
1613446Smrj 	DEVO_REV,		/* devo_rev */
1623446Smrj 	0,			/* refcnt  */
1633446Smrj 	ppb_info,		/* info */
1643446Smrj 	nulldev,		/* identify */
1653446Smrj 	ppb_probe,		/* probe */
1663446Smrj 	ppb_attach,		/* attach */
1673446Smrj 	ppb_detach,		/* detach */
1683446Smrj 	nulldev,		/* reset */
1693446Smrj 	&ppb_cb_ops,		/* driver operations */
1707656SSherry.Moore@Sun.COM 	&ppb_bus_ops,		/* bus operations */
1717656SSherry.Moore@Sun.COM 	NULL,			/* power */
1727656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* quiesce */
1733446Smrj };
1743446Smrj 
1753446Smrj /*
1763446Smrj  * Module linkage information for the kernel.
1773446Smrj  */
1783446Smrj 
1793446Smrj static struct modldrv modldrv = {
1803446Smrj 	&mod_driverops, /* Type of module */
181*10923SEvan.Yan@Sun.COM 	"Standard PCI to PCI bridge nexus driver",
1823446Smrj 	&ppb_ops,	/* driver ops */
1833446Smrj };
1843446Smrj 
1853446Smrj static struct modlinkage modlinkage = {
1863446Smrj 	MODREV_1,
1873446Smrj 	(void *)&modldrv,
1883446Smrj 	NULL
1893446Smrj };
1903446Smrj 
1913446Smrj /*
1923446Smrj  * soft state pointer and structure template:
1933446Smrj  */
1943446Smrj static void *ppb_state;
1953446Smrj 
1963446Smrj typedef struct {
1973446Smrj 	dev_info_t *dip;
1983446Smrj 	int ppb_fmcap;
1993446Smrj 	ddi_iblock_cookie_t ppb_fm_ibc;
200*10923SEvan.Yan@Sun.COM 	kmutex_t ppb_mutex;
2013446Smrj 	kmutex_t ppb_peek_poke_mutex;
2023446Smrj 	kmutex_t ppb_err_mutex;
2033446Smrj 
2043446Smrj 	/*
2053446Smrj 	 * cpr support:
2063446Smrj 	 */
2073446Smrj 	uint_t config_state_index;
2083446Smrj 	struct {
2093446Smrj 		dev_info_t *dip;
2103446Smrj 		ushort_t command;
2113446Smrj 		uchar_t cache_line_size;
2123446Smrj 		uchar_t latency_timer;
2133446Smrj 		uchar_t header_type;
2143446Smrj 		uchar_t sec_latency_timer;
2153446Smrj 		ushort_t bridge_control;
2163446Smrj 	} config_state[PCI_MAX_CHILDREN];
2176313Skrishnae 
2189921SKrishna.Elango@Sun.COM 	uint16_t parent_bus;
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 boolean_t	ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl);
2303446Smrj static int	ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd);
2313446Smrj 
2323446Smrj /*
2333446Smrj  * for <cmd> in ppb_ht_msimap_set
2343446Smrj  */
2353446Smrj #define	HT_MSIMAP_ENABLE	1
2363446Smrj #define	HT_MSIMAP_DISABLE	0
2373446Smrj 
2383446Smrj 
2393446Smrj int
2403446Smrj _init(void)
2413446Smrj {
2423446Smrj 	int e;
2433446Smrj 
2443446Smrj 	if ((e = ddi_soft_state_init(&ppb_state, sizeof (ppb_devstate_t),
2453446Smrj 	    1)) == 0 && (e = mod_install(&modlinkage)) != 0)
2463446Smrj 		ddi_soft_state_fini(&ppb_state);
2473446Smrj 	return (e);
2483446Smrj }
2493446Smrj 
2503446Smrj int
2513446Smrj _fini(void)
2523446Smrj {
2533446Smrj 	int e;
2543446Smrj 
2553446Smrj 	if ((e = mod_remove(&modlinkage)) == 0)
2563446Smrj 		ddi_soft_state_fini(&ppb_state);
2573446Smrj 	return (e);
2583446Smrj }
2593446Smrj 
2603446Smrj int
2613446Smrj _info(struct modinfo *modinfop)
2623446Smrj {
2633446Smrj 	return (mod_info(&modlinkage, modinfop));
2643446Smrj }
2653446Smrj 
2663446Smrj /*ARGSUSED*/
2673446Smrj static int
2683446Smrj ppb_probe(dev_info_t *devi)
2693446Smrj {
2703446Smrj 	return (DDI_PROBE_SUCCESS);
2713446Smrj }
2723446Smrj 
2733446Smrj /*ARGSUSED*/
2743446Smrj static int
2753446Smrj ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
2763446Smrj {
2776313Skrishnae 	dev_info_t *root = ddi_root_node();
2783446Smrj 	int instance;
2793446Smrj 	ppb_devstate_t *ppb;
2806313Skrishnae 	dev_info_t *pdip;
2813446Smrj 	ddi_acc_handle_t config_handle;
2826313Skrishnae 	char *bus;
283*10923SEvan.Yan@Sun.COM 	int ret;
2843446Smrj 
2853446Smrj 	switch (cmd) {
2863446Smrj 	case DDI_ATTACH:
2873446Smrj 
2883446Smrj 		/*
2893446Smrj 		 * Make sure the "device_type" property exists.
2903446Smrj 		 */
2913446Smrj 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
2923446Smrj 		    "device_type", "pci");
2933446Smrj 
2943446Smrj 		/*
2953446Smrj 		 * Allocate and get soft state structure.
2963446Smrj 		 */
2973446Smrj 		instance = ddi_get_instance(devi);
2983446Smrj 		if (ddi_soft_state_zalloc(ppb_state, instance) != DDI_SUCCESS)
2993446Smrj 			return (DDI_FAILURE);
3003446Smrj 		ppb = ddi_get_soft_state(ppb_state, instance);
3013446Smrj 		ppb->dip = devi;
3023446Smrj 
3033446Smrj 		/*
3043446Smrj 		 * don't enable ereports if immediate child of npe
3053446Smrj 		 */
3063446Smrj 		if (strcmp(ddi_driver_name(ddi_get_parent(devi)), "npe") == 0)
3073446Smrj 			ppb->ppb_fmcap = DDI_FM_ERRCB_CAPABLE |
3083446Smrj 			    DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
3093446Smrj 		else
3103446Smrj 			ppb->ppb_fmcap = DDI_FM_EREPORT_CAPABLE |
3113446Smrj 			    DDI_FM_ERRCB_CAPABLE | DDI_FM_ACCCHK_CAPABLE |
3123446Smrj 			    DDI_FM_DMACHK_CAPABLE;
3133446Smrj 
3143446Smrj 		ddi_fm_init(devi, &ppb->ppb_fmcap, &ppb->ppb_fm_ibc);
315*10923SEvan.Yan@Sun.COM 		mutex_init(&ppb->ppb_mutex, NULL, MUTEX_DRIVER, NULL);
3163446Smrj 		mutex_init(&ppb->ppb_err_mutex, NULL, MUTEX_DRIVER,
3173446Smrj 		    (void *)ppb->ppb_fm_ibc);
3183446Smrj 		mutex_init(&ppb->ppb_peek_poke_mutex, NULL, MUTEX_DRIVER,
3193446Smrj 		    (void *)ppb->ppb_fm_ibc);
3203446Smrj 
3213446Smrj 		if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
3223446Smrj 		    DDI_FM_EREPORT_CAPABLE))
3233446Smrj 			pci_ereport_setup(devi);
3243446Smrj 		if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
3255295Srandyf 			ddi_fm_handler_register(devi, ppb_fm_callback, NULL);
3263446Smrj 
3273446Smrj 		if (pci_config_setup(devi, &config_handle) != DDI_SUCCESS) {
3283446Smrj 			if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
3293446Smrj 				ddi_fm_handler_unregister(devi);
3303446Smrj 			if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
3313446Smrj 			    DDI_FM_EREPORT_CAPABLE))
3323446Smrj 				pci_ereport_teardown(devi);
3333446Smrj 			ddi_fm_fini(devi);
3343446Smrj 			ddi_soft_state_free(ppb_state, instance);
3353446Smrj 			return (DDI_FAILURE);
3363446Smrj 		}
3373446Smrj 
3389921SKrishna.Elango@Sun.COM 		ppb->parent_bus = PCIE_PCIECAP_DEV_TYPE_PCI_PSEUDO;
3396313Skrishnae 		for (pdip = ddi_get_parent(devi); pdip && (pdip != root) &&
3406313Skrishnae 		    (ppb->parent_bus != PCIE_PCIECAP_DEV_TYPE_PCIE_DEV);
3416313Skrishnae 		    pdip = ddi_get_parent(pdip)) {
3426313Skrishnae 			if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip,
3436313Skrishnae 			    DDI_PROP_DONTPASS, "device_type", &bus) !=
3446313Skrishnae 			    DDI_PROP_SUCCESS)
3456313Skrishnae 				break;
3466313Skrishnae 
3476313Skrishnae 			if (strcmp(bus, "pciex") == 0)
3486313Skrishnae 				ppb->parent_bus =
3496313Skrishnae 				    PCIE_PCIECAP_DEV_TYPE_PCIE_DEV;
3506313Skrishnae 
3516313Skrishnae 			ddi_prop_free(bus);
3526313Skrishnae 		}
3536313Skrishnae 
3543446Smrj 		if (ppb_support_ht_msimap == 1)
3553446Smrj 			(void) ppb_ht_msimap_set(config_handle,
3563446Smrj 			    HT_MSIMAP_ENABLE);
3573446Smrj 		else if (ppb_support_ht_msimap == -1)
3583446Smrj 			(void) ppb_ht_msimap_set(config_handle,
3593446Smrj 			    HT_MSIMAP_DISABLE);
3603446Smrj 
3613446Smrj 		pci_config_teardown(&config_handle);
3623446Smrj 
3633446Smrj 		/*
364*10923SEvan.Yan@Sun.COM 		 * Initialize hotplug support on this bus.
3653446Smrj 		 */
366*10923SEvan.Yan@Sun.COM 		if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
367*10923SEvan.Yan@Sun.COM 			ret = pcie_init(devi, NULL);
368*10923SEvan.Yan@Sun.COM 		else
369*10923SEvan.Yan@Sun.COM 			ret = pcihp_init(devi);
370*10923SEvan.Yan@Sun.COM 
371*10923SEvan.Yan@Sun.COM 		if (ret != DDI_SUCCESS) {
3725295Srandyf 			cmn_err(CE_WARN,
3735295Srandyf 			    "pci: Failed to setup hotplug framework");
374*10923SEvan.Yan@Sun.COM 			(void) ppb_detach(devi, DDI_DETACH);
375*10923SEvan.Yan@Sun.COM 			return (ret);
376*10923SEvan.Yan@Sun.COM 		}
3773446Smrj 
3783446Smrj 		ddi_report_dev(devi);
3793446Smrj 		return (DDI_SUCCESS);
3803446Smrj 
3813446Smrj 	case DDI_RESUME:
3823446Smrj 
3833446Smrj 		/*
3843446Smrj 		 * Get the soft state structure for the bridge.
3853446Smrj 		 */
3863446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
3873446Smrj 		ppb_restore_config_regs(ppb);
3883446Smrj 		return (DDI_SUCCESS);
3893446Smrj 
3903446Smrj 	default:
3913446Smrj 		break;
3923446Smrj 	}
3933446Smrj 	return (DDI_FAILURE);
3943446Smrj }
3953446Smrj 
3963446Smrj /*ARGSUSED*/
3973446Smrj static int
3983446Smrj ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
3993446Smrj {
4003446Smrj 	ppb_devstate_t *ppb;
401*10923SEvan.Yan@Sun.COM 	int		ret;
4023446Smrj 
4033446Smrj 	switch (cmd) {
4043446Smrj 	case DDI_DETACH:
4053446Smrj 		(void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type");
4063446Smrj 
4073446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
4083446Smrj 		if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
4093446Smrj 			ddi_fm_handler_unregister(devi);
4103446Smrj 		if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
4113446Smrj 		    DDI_FM_EREPORT_CAPABLE))
4123446Smrj 			pci_ereport_teardown(devi);
413*10923SEvan.Yan@Sun.COM 
414*10923SEvan.Yan@Sun.COM 		/*
415*10923SEvan.Yan@Sun.COM 		 * Uninitialize hotplug support on this bus.
416*10923SEvan.Yan@Sun.COM 		 */
417*10923SEvan.Yan@Sun.COM 		ret = (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) ?
418*10923SEvan.Yan@Sun.COM 		    pcie_uninit(devi) : pcihp_uninit(devi);
419*10923SEvan.Yan@Sun.COM 		if (ret != DDI_SUCCESS)
420*10923SEvan.Yan@Sun.COM 			return (DDI_FAILURE);
421*10923SEvan.Yan@Sun.COM 
4223446Smrj 		mutex_destroy(&ppb->ppb_peek_poke_mutex);
4233446Smrj 		mutex_destroy(&ppb->ppb_err_mutex);
424*10923SEvan.Yan@Sun.COM 		mutex_destroy(&ppb->ppb_mutex);
4253446Smrj 		ddi_fm_fini(devi);
4263446Smrj 
4273446Smrj 		/*
4283446Smrj 		 * And finally free the per-pci soft state.
4293446Smrj 		 */
4303446Smrj 		ddi_soft_state_free(ppb_state, ddi_get_instance(devi));
4313446Smrj 
4323446Smrj 		return (DDI_SUCCESS);
4333446Smrj 
4343446Smrj 	case DDI_SUSPEND:
4353446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
4363446Smrj 		ppb_save_config_regs(ppb);
4373446Smrj 		return (DDI_SUCCESS);
4383446Smrj 
4393446Smrj 	default:
4403446Smrj 		break;
4413446Smrj 	}
4423446Smrj 	return (DDI_FAILURE);
4433446Smrj }
4443446Smrj 
4453446Smrj /*ARGSUSED*/
4463446Smrj static int
4473446Smrj ppb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
4483446Smrj 	off_t offset, off_t len, caddr_t *vaddrp)
4493446Smrj {
4503446Smrj 	dev_info_t *pdip;
4513446Smrj 
4523446Smrj 	pdip = (dev_info_t *)DEVI(dip)->devi_parent;
4533446Smrj 	return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip,
4545295Srandyf 	    rdip, mp, offset, len, vaddrp));
4553446Smrj }
4563446Smrj 
4573446Smrj /*ARGSUSED*/
4583446Smrj static int
4593446Smrj ppb_ctlops(dev_info_t *dip, dev_info_t *rdip,
4603446Smrj 	ddi_ctl_enum_t ctlop, void *arg, void *result)
4613446Smrj {
4623446Smrj 	pci_regspec_t *drv_regp;
4633446Smrj 	int	reglen;
4643446Smrj 	int	rn;
4653446Smrj 	int	totreg;
4666313Skrishnae 	ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state,
4676313Skrishnae 	    ddi_get_instance(dip));
4686313Skrishnae 	struct detachspec *dsp;
4695295Srandyf 	struct attachspec *asp;
4703446Smrj 
4713446Smrj 	switch (ctlop) {
4723446Smrj 	case DDI_CTLOPS_REPORTDEV:
4733446Smrj 		if (rdip == (dev_info_t *)0)
4743446Smrj 			return (DDI_FAILURE);
4753446Smrj 		cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n",
4763446Smrj 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
4773446Smrj 		    ddi_driver_name(rdip),
4783446Smrj 		    ddi_get_instance(rdip));
4793446Smrj 		return (DDI_SUCCESS);
4803446Smrj 
4813446Smrj 	case DDI_CTLOPS_INITCHILD:
4823446Smrj 		return (ppb_initchild((dev_info_t *)arg));
4833446Smrj 
4843446Smrj 	case DDI_CTLOPS_UNINITCHILD:
4853446Smrj 		ppb_removechild((dev_info_t *)arg);
4863446Smrj 		return (DDI_SUCCESS);
4873446Smrj 
4883446Smrj 	case DDI_CTLOPS_SIDDEV:
4893446Smrj 		return (DDI_SUCCESS);
4903446Smrj 
4913446Smrj 	case DDI_CTLOPS_REGSIZE:
4923446Smrj 	case DDI_CTLOPS_NREGS:
4933446Smrj 		if (rdip == (dev_info_t *)0)
4943446Smrj 			return (DDI_FAILURE);
4953446Smrj 		break;
4963446Smrj 
4975295Srandyf 	/* X86 systems support PME wakeup from suspend */
4985295Srandyf 	case DDI_CTLOPS_ATTACH:
4996313Skrishnae 		if (!pcie_is_child(dip, rdip))
5006313Skrishnae 			return (DDI_SUCCESS);
5016313Skrishnae 
5025295Srandyf 		asp = (struct attachspec *)arg;
5036313Skrishnae 		if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) &&
5046313Skrishnae 		    (asp->when == DDI_POST) && (asp->result == DDI_SUCCESS))
5056313Skrishnae 			pf_init(rdip, (void *)ppb->ppb_fm_ibc, asp->cmd);
5066313Skrishnae 
5075295Srandyf 		if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE)
5085295Srandyf 			if (pci_pre_resume(rdip) != DDI_SUCCESS)
5095295Srandyf 				return (DDI_FAILURE);
5105295Srandyf 
5116313Skrishnae 		return (DDI_SUCCESS);
5125295Srandyf 
5135295Srandyf 	case DDI_CTLOPS_DETACH:
5146313Skrishnae 		if (!pcie_is_child(dip, rdip))
5156313Skrishnae 			return (DDI_SUCCESS);
5166313Skrishnae 
5176313Skrishnae 		dsp = (struct detachspec *)arg;
5186313Skrishnae 		if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) &&
5196313Skrishnae 		    (dsp->when == DDI_PRE))
5206313Skrishnae 			pf_fini(rdip, dsp->cmd);
5216313Skrishnae 
5226313Skrishnae 		if (dsp->cmd == DDI_SUSPEND && dsp->when == DDI_POST)
5235295Srandyf 			if (pci_post_suspend(rdip) != DDI_SUCCESS)
5245295Srandyf 				return (DDI_FAILURE);
5256313Skrishnae 
5266313Skrishnae 		return (DDI_SUCCESS);
5275295Srandyf 
5283446Smrj 	case DDI_CTLOPS_PEEK:
5293446Smrj 	case DDI_CTLOPS_POKE:
5303446Smrj 		if (strcmp(ddi_driver_name(ddi_get_parent(dip)), "npe") != 0)
5313446Smrj 			return (ddi_ctlops(dip, rdip, ctlop, arg, result));
5323446Smrj 		return (pci_peekpoke_check(dip, rdip, ctlop, arg, result,
5333446Smrj 		    ddi_ctlops, &ppb->ppb_err_mutex,
5346313Skrishnae 		    &ppb->ppb_peek_poke_mutex, ppb_peekpoke_cb));
5353446Smrj 
5363446Smrj 	default:
5373446Smrj 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
5383446Smrj 	}
5393446Smrj 
5403446Smrj 	*(int *)result = 0;
5413446Smrj 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip,
5425295Srandyf 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg",
5435295Srandyf 	    (caddr_t)&drv_regp, &reglen) != DDI_SUCCESS)
5443446Smrj 		return (DDI_FAILURE);
5453446Smrj 
5463446Smrj 	totreg = reglen / sizeof (pci_regspec_t);
5473446Smrj 	if (ctlop == DDI_CTLOPS_NREGS)
5483446Smrj 		*(int *)result = totreg;
5493446Smrj 	else if (ctlop == DDI_CTLOPS_REGSIZE) {
5503446Smrj 		rn = *(int *)arg;
5513446Smrj 		if (rn >= totreg) {
5523446Smrj 			kmem_free(drv_regp, reglen);
5533446Smrj 			return (DDI_FAILURE);
5543446Smrj 		}
5553446Smrj 		*(off_t *)result = drv_regp[rn].pci_size_low;
5563446Smrj 	}
5573446Smrj 
5583446Smrj 	kmem_free(drv_regp, reglen);
5593446Smrj 	return (DDI_SUCCESS);
5603446Smrj }
5613446Smrj 
5623446Smrj static int
5633446Smrj ppb_name_child(dev_info_t *child, char *name, int namelen)
5643446Smrj {
5653446Smrj 	pci_regspec_t *pci_rp;
5663446Smrj 	uint_t slot, func;
5673446Smrj 	char **unit_addr;
5683446Smrj 	uint_t n;
5693446Smrj 
5703446Smrj 	/*
5713446Smrj 	 * For .conf nodes, use unit-address property as name
5723446Smrj 	 */
5733446Smrj 	if (ndi_dev_is_persistent_node(child) == 0) {
5743446Smrj 		if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
5753446Smrj 		    DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) !=
5763446Smrj 		    DDI_PROP_SUCCESS) {
5773446Smrj 			cmn_err(CE_WARN,
5783446Smrj 			    "cannot find unit-address in %s.conf",
5793446Smrj 			    ddi_driver_name(child));
5803446Smrj 			return (DDI_FAILURE);
5813446Smrj 		}
5823446Smrj 		if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
5833446Smrj 			cmn_err(CE_WARN, "unit-address property in %s.conf"
5843446Smrj 			    " not well-formed", ddi_driver_name(child));
5853446Smrj 			ddi_prop_free(unit_addr);
5863446Smrj 			return (DDI_SUCCESS);
5873446Smrj 		}
5883446Smrj 		(void) snprintf(name, namelen, "%s", *unit_addr);
5893446Smrj 		ddi_prop_free(unit_addr);
5903446Smrj 		return (DDI_SUCCESS);
5913446Smrj 	}
5923446Smrj 
5933446Smrj 	/* get child "reg" property */
5943446Smrj 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
5953446Smrj 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) {
5963446Smrj 		return (DDI_FAILURE);
5973446Smrj 	}
5983446Smrj 
5993446Smrj 	/* copy the device identifications */
6003446Smrj 	slot = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
6013446Smrj 	func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
6023446Smrj 
6033446Smrj 	if (func != 0)
6043446Smrj 		(void) snprintf(name, namelen, "%x,%x", slot, func);
6053446Smrj 	else
6063446Smrj 		(void) snprintf(name, namelen, "%x", slot);
6073446Smrj 
6083446Smrj 	ddi_prop_free(pci_rp);
6093446Smrj 	return (DDI_SUCCESS);
6103446Smrj }
6113446Smrj 
6123446Smrj static int
6133446Smrj ppb_initchild(dev_info_t *child)
6143446Smrj {
6153446Smrj 	struct ddi_parent_private_data *pdptr;
6166313Skrishnae 	ppb_devstate_t *ppb;
6173446Smrj 	char name[MAXNAMELEN];
6183446Smrj 	ddi_acc_handle_t config_handle;
6193446Smrj 	ushort_t command_preserve, command;
6203446Smrj 
6216313Skrishnae 	ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
6226313Skrishnae 	    ddi_get_instance(ddi_get_parent(child)));
6236313Skrishnae 
6243446Smrj 	if (ppb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS)
6253446Smrj 		return (DDI_FAILURE);
6263446Smrj 	ddi_set_name_addr(child, name);
6273446Smrj 
6283446Smrj 	/*
6293446Smrj 	 * Pseudo nodes indicate a prototype node with per-instance
6303446Smrj 	 * properties to be merged into the real h/w device node.
6313446Smrj 	 * The interpretation of the unit-address is DD[,F]
6323446Smrj 	 * where DD is the device id and F is the function.
6333446Smrj 	 */
6343446Smrj 	if (ndi_dev_is_persistent_node(child) == 0) {
6353446Smrj 		extern int pci_allow_pseudo_children;
6363446Smrj 
6373446Smrj 		ddi_set_parent_data(child, NULL);
6383446Smrj 
6393446Smrj 		/*
6403446Smrj 		 * Try to merge the properties from this prototype
6413446Smrj 		 * node into real h/w nodes.
6423446Smrj 		 */
6433446Smrj 		if (ndi_merge_node(child, ppb_name_child) == DDI_SUCCESS) {
6443446Smrj 			/*
6453446Smrj 			 * Merged ok - return failure to remove the node.
6463446Smrj 			 */
6473446Smrj 			ddi_set_name_addr(child, NULL);
6483446Smrj 			return (DDI_FAILURE);
6493446Smrj 		}
6503446Smrj 
6513446Smrj 		/* workaround for ddivs to run under PCI */
6523446Smrj 		if (pci_allow_pseudo_children)
6533446Smrj 			return (DDI_SUCCESS);
6543446Smrj 
6553446Smrj 		/*
6563446Smrj 		 * The child was not merged into a h/w node,
6573446Smrj 		 * but there's not much we can do with it other
6583446Smrj 		 * than return failure to cause the node to be removed.
6593446Smrj 		 */
6603446Smrj 		cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
6613446Smrj 		    ddi_driver_name(child), ddi_get_name_addr(child),
6623446Smrj 		    ddi_driver_name(child));
6633446Smrj 		ddi_set_name_addr(child, NULL);
6643446Smrj 		return (DDI_NOT_WELL_FORMED);
6653446Smrj 	}
6663446Smrj 
6676313Skrishnae 	ddi_set_parent_data(child, NULL);
6686313Skrishnae 
6696313Skrishnae 	/*
6706313Skrishnae 	 * PCIe FMA specific
6716313Skrishnae 	 *
6726313Skrishnae 	 * Note: parent_data for parent is created only if this is PCI-E
6736313Skrishnae 	 * platform, for which, SG take a different route to handle device
6746313Skrishnae 	 * errors.
6756313Skrishnae 	 */
6766313Skrishnae 	if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
6776313Skrishnae 		if (pcie_init_bus(child) == NULL)
6786313Skrishnae 			return (DDI_FAILURE);
6796313Skrishnae 	}
6806313Skrishnae 
6813446Smrj 	/* transfer select properties from PROM to kernel */
6825295Srandyf 	if (ddi_getprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS,
6835295Srandyf 	    "interrupts", -1) != -1) {
6843446Smrj 		pdptr = kmem_zalloc((sizeof (struct ddi_parent_private_data) +
6853446Smrj 		    sizeof (struct intrspec)), KM_SLEEP);
6863446Smrj 		pdptr->par_intr = (struct intrspec *)(pdptr + 1);
6873446Smrj 		pdptr->par_nintr = 1;
6883446Smrj 		ddi_set_parent_data(child, pdptr);
6893446Smrj 	} else
6903446Smrj 		ddi_set_parent_data(child, NULL);
6913446Smrj 
6923446Smrj 	if (pci_config_setup(child, &config_handle) != DDI_SUCCESS)
6933446Smrj 		return (DDI_FAILURE);
6943446Smrj 
6953446Smrj 	/*
6963446Smrj 	 * Support for the "command-preserve" property.
6973446Smrj 	 */
6983446Smrj 	command_preserve = ddi_prop_get_int(DDI_DEV_T_ANY, child,
6993446Smrj 	    DDI_PROP_DONTPASS, "command-preserve", 0);
7003446Smrj 	command = pci_config_get16(config_handle, PCI_CONF_COMM);
7013446Smrj 	command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB);
7023446Smrj 	command |= (ppb_command_default & ~command_preserve);
7033446Smrj 	pci_config_put16(config_handle, PCI_CONF_COMM, command);
7043446Smrj 
7053446Smrj 	pci_config_teardown(&config_handle);
7063446Smrj 	return (DDI_SUCCESS);
7073446Smrj }
7083446Smrj 
7093446Smrj static void
7103446Smrj ppb_removechild(dev_info_t *dip)
7113446Smrj {
7123446Smrj 	struct ddi_parent_private_data *pdptr;
7136313Skrishnae 	ppb_devstate_t *ppb;
7143446Smrj 
7156313Skrishnae 	ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
7166313Skrishnae 	    ddi_get_instance(ddi_get_parent(dip)));
7176313Skrishnae 
7186313Skrishnae 	if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
7196313Skrishnae 		pcie_fini_bus(dip);
7206313Skrishnae 	else if ((pdptr = ddi_get_parent_data(dip)) != NULL) {
7213446Smrj 		kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec)));
7223446Smrj 		ddi_set_parent_data(dip, NULL);
7233446Smrj 	}
7243446Smrj 	ddi_set_name_addr(dip, NULL);
7253446Smrj 
7263446Smrj 	/*
7273446Smrj 	 * Strip the node to properly convert it back to prototype form
7283446Smrj 	 */
7293446Smrj 	ddi_remove_minor_node(dip, NULL);
7303446Smrj 
7313446Smrj 	impl_rem_dev_props(dip);
7323446Smrj }
7333446Smrj 
7343446Smrj /*
7353446Smrj  * ppb_save_config_regs
7363446Smrj  *
7373446Smrj  * This routine saves the state of the configuration registers of all
7383446Smrj  * the child nodes of each PBM.
7393446Smrj  *
7403446Smrj  * used by: ppb_detach() on suspends
7413446Smrj  *
7423446Smrj  * return value: none
7433446Smrj  */
7443446Smrj static void
7453446Smrj ppb_save_config_regs(ppb_devstate_t *ppb_p)
7463446Smrj {
7473446Smrj 	int i;
7483446Smrj 	dev_info_t *dip;
7493446Smrj 	ddi_acc_handle_t config_handle;
7503446Smrj 
7513446Smrj 	for (i = 0, dip = ddi_get_child(ppb_p->dip); dip != NULL;
7525295Srandyf 	    i++, dip = ddi_get_next_sibling(dip)) {
7533446Smrj 
7543446Smrj 		if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
7553446Smrj 			cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
7565295Srandyf 			    ddi_driver_name(ppb_p->dip),
7575295Srandyf 			    ddi_get_instance(ppb_p->dip),
7585295Srandyf 			    ddi_driver_name(dip),
7595295Srandyf 			    ddi_get_instance(dip));
7603446Smrj 			continue;
7613446Smrj 		}
7623446Smrj 
7633446Smrj 		ppb_p->config_state[i].dip = dip;
7643446Smrj 		ppb_p->config_state[i].command =
7655295Srandyf 		    pci_config_get16(config_handle, PCI_CONF_COMM);
7663446Smrj 		pci_config_teardown(&config_handle);
7673446Smrj 	}
7683446Smrj 	ppb_p->config_state_index = i;
7693446Smrj }
7703446Smrj 
7713446Smrj 
7723446Smrj /*
7733446Smrj  * ppb_restore_config_regs
7743446Smrj  *
7753446Smrj  * This routine restores the state of the configuration registers of all
7763446Smrj  * the child nodes of each PBM.
7773446Smrj  *
7783446Smrj  * used by: ppb_attach() on resume
7793446Smrj  *
7803446Smrj  * return value: none
7813446Smrj  */
7823446Smrj static void
7833446Smrj ppb_restore_config_regs(ppb_devstate_t *ppb_p)
7843446Smrj {
7853446Smrj 	int i;
7863446Smrj 	dev_info_t *dip;
7873446Smrj 	ddi_acc_handle_t config_handle;
7883446Smrj 
7893446Smrj 	for (i = 0; i < ppb_p->config_state_index; i++) {
7903446Smrj 		dip = ppb_p->config_state[i].dip;
7913446Smrj 		if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
7923446Smrj 			cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
7935295Srandyf 			    ddi_driver_name(ppb_p->dip),
7945295Srandyf 			    ddi_get_instance(ppb_p->dip),
7955295Srandyf 			    ddi_driver_name(dip),
7965295Srandyf 			    ddi_get_instance(dip));
7973446Smrj 			continue;
7983446Smrj 		}
7993446Smrj 		pci_config_put16(config_handle, PCI_CONF_COMM,
8005295Srandyf 		    ppb_p->config_state[i].command);
8013446Smrj 		pci_config_teardown(&config_handle);
8023446Smrj 	}
8033446Smrj }
8043446Smrj 
8053446Smrj 
8063446Smrj static boolean_t
8073446Smrj ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl)
8083446Smrj {
8099970SJimmy.Vetayases@Sun.COM 	uint16_t ptr;
8103446Smrj 
8119970SJimmy.Vetayases@Sun.COM 	if (pci_htcap_locate(cfg_hdl,
8129970SJimmy.Vetayases@Sun.COM 	    PCI_HTCAP_TYPE_MASK | PCI_HTCAP_MSIMAP_ENABLE_MASK,
8139970SJimmy.Vetayases@Sun.COM 	    PCI_HTCAP_MSIMAP_TYPE | PCI_HTCAP_MSIMAP_ENABLE, &ptr) !=
8149970SJimmy.Vetayases@Sun.COM 	    DDI_SUCCESS)
8153446Smrj 		return (B_FALSE);
8163446Smrj 
8173446Smrj 	return (B_TRUE);
8183446Smrj }
8193446Smrj 
8203446Smrj 
8213446Smrj static int
8223446Smrj ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd)
8233446Smrj {
8249970SJimmy.Vetayases@Sun.COM 	uint16_t ptr;
8253446Smrj 	uint16_t reg;
8263446Smrj 
8279970SJimmy.Vetayases@Sun.COM 	if (pci_htcap_locate(cfg_hdl, PCI_HTCAP_TYPE_MASK,
8289970SJimmy.Vetayases@Sun.COM 	    PCI_HTCAP_MSIMAP_TYPE, &ptr) != DDI_SUCCESS)
8293446Smrj 		return (0);
8303446Smrj 
8313446Smrj 	reg = pci_config_get16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF);
8323446Smrj 	switch (cmd) {
8333446Smrj 	case HT_MSIMAP_ENABLE:
8349970SJimmy.Vetayases@Sun.COM 		reg |= PCI_HTCAP_MSIMAP_ENABLE;
8353446Smrj 		break;
8363446Smrj 	case HT_MSIMAP_DISABLE:
8373446Smrj 	default:
8389970SJimmy.Vetayases@Sun.COM 		reg &= ~(uint16_t)PCI_HTCAP_MSIMAP_ENABLE;
8393446Smrj 	}
8403446Smrj 
8413446Smrj 	pci_config_put16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF, reg);
8423446Smrj 	return (1);
8433446Smrj }
8443446Smrj 
8453446Smrj 
8463446Smrj /*
8473446Smrj  * intercept certain interrupt services to handle special cases
8483446Smrj  */
8493446Smrj static int
8503446Smrj ppb_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
8513446Smrj     ddi_intr_handle_impl_t *hdlp, void *result)
8523446Smrj {
8533446Smrj 	ddi_acc_handle_t cfg_hdl;
8543446Smrj 	int rv = DDI_SUCCESS;
8553446Smrj 
8563446Smrj 	if (intr_op != DDI_INTROP_SUPPORTED_TYPES)
8573446Smrj 		return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result));
8583446Smrj 
8593446Smrj 	DDI_INTR_NEXDBG((CE_CONT,
8603446Smrj 	    "ppb_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
8613446Smrj 	    (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
8623446Smrj 
8633446Smrj 	/* Fixed interrupt is supported by default */
8643446Smrj 	*(int *)result = DDI_INTR_TYPE_FIXED;
8653446Smrj 
8663446Smrj 	if (ppb_support_msi == -1) {
8673446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
8683446Smrj 		    "ppb_intr_ops: MSI is not allowed\n"));
8693446Smrj 		goto OUT;
8703446Smrj 	}
8713446Smrj 
8723446Smrj 	if (ppb_support_msi == 1) {
8733446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
8743446Smrj 		    "ppb_intr_ops: MSI is always allowed\n"));
8753446Smrj 		rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result);
8763446Smrj 		goto OUT;
8773446Smrj 	}
8783446Smrj 
8793446Smrj 	if (pci_config_setup(pdip, &cfg_hdl) != DDI_SUCCESS) {
8803446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
8813446Smrj 		    "ppb_intr_ops: pci_config_setup() failed\n"));
8823446Smrj 		goto OUT;
8833446Smrj 	}
8843446Smrj 
8853446Smrj 	/*
8863446Smrj 	 * check for hypertransport msi mapping capability
8873446Smrj 	 */
8883446Smrj 	if (ppb_ht_msimap_check(cfg_hdl)) {
8893446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
8903446Smrj 		    "ppb_intr_ops: HT MSI mapping enabled\n"));
8913446Smrj 		rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result);
8923446Smrj 	}
8933446Smrj 
8943446Smrj 	/*
8953446Smrj 	 * if we add failure conditions after pci_config_setup, move this to
8963446Smrj 	 * OUT and use an extra flag to indicate the need to teardown cfg_hdl
8973446Smrj 	 */
8983446Smrj 	pci_config_teardown(&cfg_hdl);
8993446Smrj 
9003446Smrj OUT:
9013446Smrj 	DDI_INTR_NEXDBG((CE_CONT,
9023446Smrj 	    "ppb_intr_ops: rdip 0x%p, returns supported types: 0x%x\n",
9033446Smrj 	    (void *)rdip, *(int *)result));
9043446Smrj 	return (rv);
9053446Smrj }
9063446Smrj 
907*10923SEvan.Yan@Sun.COM /* ARGSUSED */
9083446Smrj static int
9093446Smrj ppb_open(dev_t *devp, int flags, int otyp, cred_t *credp)
9103446Smrj {
911*10923SEvan.Yan@Sun.COM 	int		instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(*devp));
912*10923SEvan.Yan@Sun.COM 	ppb_devstate_t	*ppb_p = ddi_get_soft_state(ppb_state, instance);
913*10923SEvan.Yan@Sun.COM 	int	rv;
914*10923SEvan.Yan@Sun.COM 
915*10923SEvan.Yan@Sun.COM 	if (ppb_p == NULL)
916*10923SEvan.Yan@Sun.COM 		return (ENXIO);
917*10923SEvan.Yan@Sun.COM 
918*10923SEvan.Yan@Sun.COM 	/*
919*10923SEvan.Yan@Sun.COM 	 * Ioctls will be handled by PCI Express framework for all
920*10923SEvan.Yan@Sun.COM 	 * PCIe platforms
921*10923SEvan.Yan@Sun.COM 	 */
922*10923SEvan.Yan@Sun.COM 	if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
923*10923SEvan.Yan@Sun.COM 		mutex_enter(&ppb_p->ppb_mutex);
924*10923SEvan.Yan@Sun.COM 		rv = pcie_open(ppb_p->dip, devp, flags, otyp, credp);
925*10923SEvan.Yan@Sun.COM 		mutex_exit(&ppb_p->ppb_mutex);
926*10923SEvan.Yan@Sun.COM 		return (rv);
927*10923SEvan.Yan@Sun.COM 	}
928*10923SEvan.Yan@Sun.COM 
9293446Smrj 	return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp));
9303446Smrj }
9313446Smrj 
932*10923SEvan.Yan@Sun.COM /* ARGSUSED */
9333446Smrj static int
9343446Smrj ppb_close(dev_t dev, int flags, int otyp, cred_t *credp)
9353446Smrj {
936*10923SEvan.Yan@Sun.COM 	int		instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
937*10923SEvan.Yan@Sun.COM 	ppb_devstate_t	*ppb_p = ddi_get_soft_state(ppb_state, instance);
938*10923SEvan.Yan@Sun.COM 	int	rv;
939*10923SEvan.Yan@Sun.COM 
940*10923SEvan.Yan@Sun.COM 	if (ppb_p == NULL)
941*10923SEvan.Yan@Sun.COM 		return (ENXIO);
942*10923SEvan.Yan@Sun.COM 
943*10923SEvan.Yan@Sun.COM 	mutex_enter(&ppb_p->ppb_mutex);
944*10923SEvan.Yan@Sun.COM 	/*
945*10923SEvan.Yan@Sun.COM 	 * Ioctls will be handled by PCI Express framework for all
946*10923SEvan.Yan@Sun.COM 	 * PCIe platforms
947*10923SEvan.Yan@Sun.COM 	 */
948*10923SEvan.Yan@Sun.COM 	if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
949*10923SEvan.Yan@Sun.COM 		rv = pcie_close(ppb_p->dip, dev, flags, otyp, credp);
950*10923SEvan.Yan@Sun.COM 		mutex_exit(&ppb_p->ppb_mutex);
951*10923SEvan.Yan@Sun.COM 		return (rv);
952*10923SEvan.Yan@Sun.COM 	}
953*10923SEvan.Yan@Sun.COM 
954*10923SEvan.Yan@Sun.COM 	mutex_exit(&ppb_p->ppb_mutex);
9553446Smrj 	return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp));
9563446Smrj }
9573446Smrj 
958*10923SEvan.Yan@Sun.COM /*
959*10923SEvan.Yan@Sun.COM  * ppb_ioctl: devctl hotplug controls
960*10923SEvan.Yan@Sun.COM  */
961*10923SEvan.Yan@Sun.COM /* ARGSUSED */
9623446Smrj static int
963*10923SEvan.Yan@Sun.COM ppb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
964*10923SEvan.Yan@Sun.COM 	int *rvalp)
9653446Smrj {
966*10923SEvan.Yan@Sun.COM 	int		instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
967*10923SEvan.Yan@Sun.COM 	ppb_devstate_t	*ppb_p = ddi_get_soft_state(ppb_state, instance);
968*10923SEvan.Yan@Sun.COM 
969*10923SEvan.Yan@Sun.COM 	if (ppb_p == NULL)
970*10923SEvan.Yan@Sun.COM 		return (ENXIO);
971*10923SEvan.Yan@Sun.COM 
972*10923SEvan.Yan@Sun.COM 	/*
973*10923SEvan.Yan@Sun.COM 	 * Ioctls will be handled by PCI Express framework for all
974*10923SEvan.Yan@Sun.COM 	 * PCIe platforms
975*10923SEvan.Yan@Sun.COM 	 */
976*10923SEvan.Yan@Sun.COM 	if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
977*10923SEvan.Yan@Sun.COM 		return (pcie_ioctl(ppb_p->dip, dev, cmd, arg, mode, credp,
978*10923SEvan.Yan@Sun.COM 		    rvalp));
979*10923SEvan.Yan@Sun.COM 
9803446Smrj 	return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, credp,
9813446Smrj 	    rvalp));
9823446Smrj }
9833446Smrj 
9843446Smrj static int
985*10923SEvan.Yan@Sun.COM ppb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int flags,
986*10923SEvan.Yan@Sun.COM     char *name, caddr_t valuep, int *lengthp)
9873446Smrj {
988*10923SEvan.Yan@Sun.COM 	int		instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
989*10923SEvan.Yan@Sun.COM 	ppb_devstate_t	*ppb_p = ddi_get_soft_state(ppb_state, instance);
990*10923SEvan.Yan@Sun.COM 
991*10923SEvan.Yan@Sun.COM 	if (ppb_p == NULL)
992*10923SEvan.Yan@Sun.COM 		return (ENXIO);
993*10923SEvan.Yan@Sun.COM 
994*10923SEvan.Yan@Sun.COM 	if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
995*10923SEvan.Yan@Sun.COM 		return (pcie_prop_op(dev, dip, prop_op, flags, name,
996*10923SEvan.Yan@Sun.COM 		    valuep, lengthp));
997*10923SEvan.Yan@Sun.COM 
9983446Smrj 	return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags,
9993446Smrj 	    name, valuep, lengthp));
10003446Smrj }
10013446Smrj 
10023446Smrj static int
10033446Smrj ppb_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
10043446Smrj {
1005*10923SEvan.Yan@Sun.COM 	minor_t		minor = getminor((dev_t)arg);
1006*10923SEvan.Yan@Sun.COM 	int		instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
1007*10923SEvan.Yan@Sun.COM 	ppb_devstate_t	*ppb_p = ddi_get_soft_state(ppb_state, instance);
1008*10923SEvan.Yan@Sun.COM 
1009*10923SEvan.Yan@Sun.COM 	if (ppb_p == NULL)
1010*10923SEvan.Yan@Sun.COM 		return (DDI_FAILURE);
1011*10923SEvan.Yan@Sun.COM 
1012*10923SEvan.Yan@Sun.COM 	if (ppb_p->parent_bus != PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
1013*10923SEvan.Yan@Sun.COM 		return (pcihp_info(dip, cmd, arg, result));
1014*10923SEvan.Yan@Sun.COM 
1015*10923SEvan.Yan@Sun.COM 	switch (cmd) {
1016*10923SEvan.Yan@Sun.COM 	default:
1017*10923SEvan.Yan@Sun.COM 		return (DDI_FAILURE);
1018*10923SEvan.Yan@Sun.COM 
1019*10923SEvan.Yan@Sun.COM 	case DDI_INFO_DEVT2INSTANCE:
1020*10923SEvan.Yan@Sun.COM 		*result = (void *)(uintptr_t)instance;
1021*10923SEvan.Yan@Sun.COM 		return (DDI_SUCCESS);
1022*10923SEvan.Yan@Sun.COM 
1023*10923SEvan.Yan@Sun.COM 	case DDI_INFO_DEVT2DEVINFO:
1024*10923SEvan.Yan@Sun.COM 		if (ppb_p == NULL)
1025*10923SEvan.Yan@Sun.COM 			return (DDI_FAILURE);
1026*10923SEvan.Yan@Sun.COM 		*result = (void *)ppb_p->dip;
1027*10923SEvan.Yan@Sun.COM 		return (DDI_SUCCESS);
1028*10923SEvan.Yan@Sun.COM 	}
10293446Smrj }
10303446Smrj 
10316313Skrishnae void ppb_peekpoke_cb(dev_info_t *dip, ddi_fm_error_t *derr) {
10326313Skrishnae 	(void) pci_ereport_post(dip, derr, NULL);
10336313Skrishnae }
10346313Skrishnae 
10353446Smrj /*ARGSUSED*/
10363446Smrj static int
10373446Smrj ppb_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap,
10383446Smrj     ddi_iblock_cookie_t *ibc)
10393446Smrj {
10403446Smrj 	ppb_devstate_t  *ppb = ddi_get_soft_state(ppb_state,
10413446Smrj 	    ddi_get_instance(dip));
10423446Smrj 
10433446Smrj 	ASSERT(ibc != NULL);
10443446Smrj 	*ibc = ppb->ppb_fm_ibc;
10453446Smrj 
10463446Smrj 	return (ppb->ppb_fmcap);
10473446Smrj }
10483446Smrj 
10493446Smrj /*ARGSUSED*/
10503446Smrj static int
10513446Smrj ppb_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *no_used)
10523446Smrj {
10533446Smrj 	ppb_devstate_t  *ppb = ddi_get_soft_state(ppb_state,
10543446Smrj 	    ddi_get_instance(dip));
10553446Smrj 
10563446Smrj 	mutex_enter(&ppb->ppb_err_mutex);
10573446Smrj 	pci_ereport_post(dip, derr, NULL);
10583446Smrj 	mutex_exit(&ppb->ppb_err_mutex);
10593446Smrj 	return (derr->fme_status);
10603446Smrj }
1061