xref: /illumos-gate/usr/src/uts/i86pc/io/pciex/npe.c (revision fb876f96)
170025d76Sjohnny /*
270025d76Sjohnny  * CDDL HEADER START
370025d76Sjohnny  *
470025d76Sjohnny  * The contents of this file are subject to the terms of the
500d0963fSdilpreet  * Common Development and Distribution License (the "License").
600d0963fSdilpreet  * You may not use this file except in compliance with the License.
770025d76Sjohnny  *
870025d76Sjohnny  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
970025d76Sjohnny  * or http://www.opensolaris.org/os/licensing.
1070025d76Sjohnny  * See the License for the specific language governing permissions
1170025d76Sjohnny  * and limitations under the License.
1270025d76Sjohnny  *
1370025d76Sjohnny  * When distributing Covered Code, include this CDDL HEADER in each
1470025d76Sjohnny  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1570025d76Sjohnny  * If applicable, add the following below this CDDL HEADER, with the
1670025d76Sjohnny  * fields enclosed by brackets "[]" replaced with your own identifying
1770025d76Sjohnny  * information: Portions Copyright [yyyy] [name of copyright owner]
1870025d76Sjohnny  *
1970025d76Sjohnny  * CDDL HEADER END
2070025d76Sjohnny  */
2170025d76Sjohnny 
2270025d76Sjohnny /*
23fc256490SJason Beloro  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2470025d76Sjohnny  * Use is subject to license terms.
2570025d76Sjohnny  */
2670025d76Sjohnny 
27cd21e7c5SGarrett D'Amore /*
28cd21e7c5SGarrett D'Amore  * Copyright 2012 Garrett D'Amore <garrett@damore.org>.  All rights reserved.
29ffb64830SJordan Paige Hendricks  * Copyright 2019 Joyent, Inc.
30*fb876f96SRobert Mustacchi  * Copyright 2024 Oxide Computer Company
31cd21e7c5SGarrett D'Amore  */
32cd21e7c5SGarrett D'Amore 
3370025d76Sjohnny /*
34ffb64830SJordan Paige Hendricks  *	npe (Nexus PCIe driver): Host to PCI-Express local bus driver
35ffb64830SJordan Paige Hendricks  *
36ffb64830SJordan Paige Hendricks  *	npe serves as the driver for PCIe Root Complexes and as the nexus driver
37bbf21555SRichard Lowe  *	for PCIe devices. See also: npe(4D). For more information about hotplug,
38ffb64830SJordan Paige Hendricks  *	see the big theory statement at uts/common/os/ddi_hp_impl.c.
39ffb64830SJordan Paige Hendricks  *
40ffb64830SJordan Paige Hendricks  *
41ffb64830SJordan Paige Hendricks  *	NDI EVENT HANDLING SUPPORT
42ffb64830SJordan Paige Hendricks  *
43ffb64830SJordan Paige Hendricks  *	npe supports NDI event handling. The only available event is surprise
44ffb64830SJordan Paige Hendricks  *	removal of a device. Child drivers can register surprise removal event
45ffb64830SJordan Paige Hendricks  *	callbacks by requesting an event cookie using ddi_get_eventcookie for
46ffb64830SJordan Paige Hendricks  *	the DDI_DEVI_REMOVE_EVENT and add their callback using
47ffb64830SJordan Paige Hendricks  *	ddi_add_event_handler. For an example, see the nvme driver in
48ffb64830SJordan Paige Hendricks  *	uts/common/io/nvme/nvme.c.
49ffb64830SJordan Paige Hendricks  *
50ffb64830SJordan Paige Hendricks  *	The NDI events in npe are retrieved using NDI_EVENT_NOPASS, which
51ffb64830SJordan Paige Hendricks  *	prevent them from being propagated up the tree once they reach the npe's
52ffb64830SJordan Paige Hendricks  *	bus_get_eventcookie operations. This is important because npe maintains
53ffb64830SJordan Paige Hendricks  *	the state of PCIe devices and their receptacles, via the PCIe hotplug
54ffb64830SJordan Paige Hendricks  *	controller driver (pciehpc).
55ffb64830SJordan Paige Hendricks  *
56ffb64830SJordan Paige Hendricks  *	Hot removal events are ultimately posted by the PCIe hotplug controller
57ffb64830SJordan Paige Hendricks  *	interrupt handler for hotplug events. Events are posted using the
58ffb64830SJordan Paige Hendricks  *	ndi_post_event interface.
5970025d76Sjohnny  */
6070025d76Sjohnny 
6170025d76Sjohnny #include <sys/conf.h>
6270025d76Sjohnny #include <sys/modctl.h>
6326947304SEvan Yan #include <sys/file.h>
6470025d76Sjohnny #include <sys/pci_impl.h>
65eae2e508Skrishnae #include <sys/pcie_impl.h>
6670025d76Sjohnny #include <sys/sysmacros.h>
6770025d76Sjohnny #include <sys/ddi_intr.h>
6870025d76Sjohnny #include <sys/sunndi.h>
6900d0963fSdilpreet #include <sys/sunddi.h>
7000d0963fSdilpreet #include <sys/ddifm.h>
7100d0963fSdilpreet #include <sys/ndifm.h>
7200d0963fSdilpreet #include <sys/fm/util.h>
7326947304SEvan Yan #include <sys/hotplug/pci/pcie_hp.h>
74c333dd99Sdm #include <io/pci/pci_tools_ext.h>
75eae2e508Skrishnae #include <io/pci/pci_common.h>
76eae2e508Skrishnae #include <io/pciex/pcie_nvidia.h>
7770025d76Sjohnny 
7814f1dfe8SSeth Goldberg /*
7914f1dfe8SSeth Goldberg  * Helper Macros
8014f1dfe8SSeth Goldberg  */
8114f1dfe8SSeth Goldberg #define	NPE_IS_HANDLE_FOR_STDCFG_ACC(hp) \
8214f1dfe8SSeth Goldberg 	((hp) != NULL &&						\
8314f1dfe8SSeth Goldberg 	((ddi_acc_hdl_t *)(hp))->ah_platform_private != NULL &&		\
8414f1dfe8SSeth Goldberg 	(((ddi_acc_impl_t *)((ddi_acc_hdl_t *)(hp))->			\
8514f1dfe8SSeth Goldberg 	ah_platform_private)->						\
8614f1dfe8SSeth Goldberg 	    ahi_acc_attr &(DDI_ACCATTR_CPU_VADDR|DDI_ACCATTR_CONFIG_SPACE)) \
8714f1dfe8SSeth Goldberg 		== DDI_ACCATTR_CONFIG_SPACE)
8814f1dfe8SSeth Goldberg 
8970025d76Sjohnny /*
9070025d76Sjohnny  * Bus Operation functions
9170025d76Sjohnny  */
9270025d76Sjohnny static int	npe_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *,
9370025d76Sjohnny 		    off_t, off_t, caddr_t *);
9470025d76Sjohnny static int	npe_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
9570025d76Sjohnny 		    void *, void *);
9670025d76Sjohnny static int	npe_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t,
9770025d76Sjohnny 		    ddi_intr_handle_impl_t *, void *);
9800d0963fSdilpreet static int	npe_fm_init(dev_info_t *, dev_info_t *, int,
9900d0963fSdilpreet 		    ddi_iblock_cookie_t *);
100ffb64830SJordan Paige Hendricks static int	npe_bus_get_eventcookie(dev_info_t *, dev_info_t *, char *,
101ffb64830SJordan Paige Hendricks 		    ddi_eventcookie_t *);
102ffb64830SJordan Paige Hendricks static int	npe_bus_add_eventcall(dev_info_t *, dev_info_t *,
103ffb64830SJordan Paige Hendricks 		    ddi_eventcookie_t, void (*)(dev_info_t *,
104ffb64830SJordan Paige Hendricks 		    ddi_eventcookie_t, void *, void *),
105ffb64830SJordan Paige Hendricks 		    void *, ddi_callback_id_t *);
106ffb64830SJordan Paige Hendricks static int	npe_bus_remove_eventcall(dev_info_t *, ddi_callback_id_t);
107ffb64830SJordan Paige Hendricks static int	npe_bus_post_event(dev_info_t *, dev_info_t *,
108ffb64830SJordan Paige Hendricks 		    ddi_eventcookie_t, void *);
10900d0963fSdilpreet 
11000d0963fSdilpreet static int	npe_fm_callback(dev_info_t *, ddi_fm_error_t *, const void *);
11170025d76Sjohnny 
112eae2e508Skrishnae /*
113eae2e508Skrishnae  * Disable URs and Received MA for all PCIe devices.  Until x86 SW is changed so
114eae2e508Skrishnae  * that random drivers do not do PIO accesses on devices that it does not own,
115eae2e508Skrishnae  * these error bits must be disabled.  SERR must also be disabled if URs have
116eae2e508Skrishnae  * been masked.
117eae2e508Skrishnae  */
118eae2e508Skrishnae uint32_t	npe_aer_uce_mask = PCIE_AER_UCE_UR;
119eae2e508Skrishnae uint32_t	npe_aer_ce_mask = 0;
120eae2e508Skrishnae uint32_t	npe_aer_suce_mask = PCIE_AER_SUCE_RCVD_MA;
121eae2e508Skrishnae 
12270025d76Sjohnny struct bus_ops npe_bus_ops = {
12370025d76Sjohnny 	BUSO_REV,
12470025d76Sjohnny 	npe_bus_map,
12570025d76Sjohnny 	NULL,
12670025d76Sjohnny 	NULL,
12770025d76Sjohnny 	NULL,
12870025d76Sjohnny 	i_ddi_map_fault,
129cd21e7c5SGarrett D'Amore 	NULL,
13070025d76Sjohnny 	ddi_dma_allochdl,
13170025d76Sjohnny 	ddi_dma_freehdl,
13270025d76Sjohnny 	ddi_dma_bindhdl,
13370025d76Sjohnny 	ddi_dma_unbindhdl,
13470025d76Sjohnny 	ddi_dma_flush,
13570025d76Sjohnny 	ddi_dma_win,
13670025d76Sjohnny 	ddi_dma_mctl,
13770025d76Sjohnny 	npe_ctlops,
13870025d76Sjohnny 	ddi_bus_prop_op,
139ffb64830SJordan Paige Hendricks 	npe_bus_get_eventcookie,
140ffb64830SJordan Paige Hendricks 	npe_bus_add_eventcall,
141ffb64830SJordan Paige Hendricks 	npe_bus_remove_eventcall,
142ffb64830SJordan Paige Hendricks 	npe_bus_post_event,
14326947304SEvan Yan 	0,			/* (*bus_intr_ctl)(); */
14426947304SEvan Yan 	0,			/* (*bus_config)(); */
14526947304SEvan Yan 	0,			/* (*bus_unconfig)(); */
14626947304SEvan Yan 	npe_fm_init,		/* (*bus_fm_init)(); */
14726947304SEvan Yan 	NULL,			/* (*bus_fm_fini)(); */
14826947304SEvan Yan 	NULL,			/* (*bus_fm_access_enter)(); */
14926947304SEvan Yan 	NULL,			/* (*bus_fm_access_exit)(); */
15026947304SEvan Yan 	NULL,			/* (*bus_power)(); */
15126947304SEvan Yan 	npe_intr_ops,		/* (*bus_intr_op)(); */
15226947304SEvan Yan 	pcie_hp_common_ops	/* (*bus_hp_op)(); */
15370025d76Sjohnny };
15470025d76Sjohnny 
15570025d76Sjohnny static int	npe_open(dev_t *, int, int, cred_t *);
15670025d76Sjohnny static int	npe_close(dev_t, int, int, cred_t *);
15770025d76Sjohnny static int	npe_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
15870025d76Sjohnny 
15970025d76Sjohnny struct cb_ops npe_cb_ops = {
16070025d76Sjohnny 	npe_open,			/* open */
16170025d76Sjohnny 	npe_close,			/* close */
16270025d76Sjohnny 	nodev,				/* strategy */
16370025d76Sjohnny 	nodev,				/* print */
16470025d76Sjohnny 	nodev,				/* dump */
16570025d76Sjohnny 	nodev,				/* read */
16670025d76Sjohnny 	nodev,				/* write */
16770025d76Sjohnny 	npe_ioctl,			/* ioctl */
16870025d76Sjohnny 	nodev,				/* devmap */
16970025d76Sjohnny 	nodev,				/* mmap */
17070025d76Sjohnny 	nodev,				/* segmap */
17170025d76Sjohnny 	nochpoll,			/* poll */
17226947304SEvan Yan 	pcie_prop_op,			/* cb_prop_op */
17370025d76Sjohnny 	NULL,				/* streamtab */
17470025d76Sjohnny 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
17570025d76Sjohnny 	CB_REV,				/* rev */
17670025d76Sjohnny 	nodev,				/* int (*cb_aread)() */
17770025d76Sjohnny 	nodev				/* int (*cb_awrite)() */
17870025d76Sjohnny };
17970025d76Sjohnny 
18070025d76Sjohnny 
18170025d76Sjohnny /*
18270025d76Sjohnny  * Device Node Operation functions
18370025d76Sjohnny  */
18470025d76Sjohnny static int	npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
18570025d76Sjohnny static int	npe_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
18626947304SEvan Yan static int	npe_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
18770025d76Sjohnny 
18870025d76Sjohnny struct dev_ops npe_ops = {
18970025d76Sjohnny 	DEVO_REV,		/* devo_rev */
19070025d76Sjohnny 	0,			/* refcnt  */
191247980beSanish 	npe_info,		/* info */
19270025d76Sjohnny 	nulldev,		/* identify */
19370025d76Sjohnny 	nulldev,		/* probe */
19470025d76Sjohnny 	npe_attach,		/* attach */
19570025d76Sjohnny 	npe_detach,		/* detach */
19670025d76Sjohnny 	nulldev,		/* reset */
19770025d76Sjohnny 	&npe_cb_ops,		/* driver operations */
19819397407SSherry Moore 	&npe_bus_ops,		/* bus operations */
19919397407SSherry Moore 	NULL,			/* power */
20019397407SSherry Moore 	ddi_quiesce_not_needed,		/* quiesce */
20170025d76Sjohnny };
20270025d76Sjohnny 
20370025d76Sjohnny /*
20470025d76Sjohnny  * Internal routines in support of particular npe_ctlops.
20570025d76Sjohnny  */
20670025d76Sjohnny static int npe_removechild(dev_info_t *child);
20770025d76Sjohnny static int npe_initchild(dev_info_t *child);
20870025d76Sjohnny 
20970025d76Sjohnny /*
21070025d76Sjohnny  * External support routine
21170025d76Sjohnny  */
212337fc9e2Sanish extern void	npe_ck804_fix_aer_ptr(ddi_acc_handle_t cfg_hdl);
2137a23d100Sanish extern int	npe_disable_empty_bridges_workaround(dev_info_t *child);
214a2de976fSPavel Potoplyak extern void	npe_nvidia_error_workaround(ddi_acc_handle_t cfg_hdl);
215a2de976fSPavel Potoplyak extern void	npe_intel_error_workaround(ddi_acc_handle_t cfg_hdl);
21614f1dfe8SSeth Goldberg extern boolean_t npe_is_mmcfg_supported(dev_info_t *dip);
217cb7ea99dSJimmy Vetayases extern void	npe_enable_htmsi_children(dev_info_t *dip);
218cb7ea99dSJimmy Vetayases extern int	npe_save_htconfig_children(dev_info_t *dip);
219cb7ea99dSJimmy Vetayases extern int	npe_restore_htconfig_children(dev_info_t *dip);
22070025d76Sjohnny 
22170025d76Sjohnny /*
22270025d76Sjohnny  * Module linkage information for the kernel.
22370025d76Sjohnny  */
22470025d76Sjohnny static struct modldrv modldrv = {
22526947304SEvan Yan 	&mod_driverops,				/* Type of module */
22626947304SEvan Yan 	"Host to PCIe nexus driver",		/* Name of module */
22726947304SEvan Yan 	&npe_ops,				/* driver ops */
22870025d76Sjohnny };
22970025d76Sjohnny 
23070025d76Sjohnny static struct modlinkage modlinkage = {
23170025d76Sjohnny 	MODREV_1,
23270025d76Sjohnny 	(void *)&modldrv,
23370025d76Sjohnny 	NULL
23470025d76Sjohnny };
23570025d76Sjohnny 
23670025d76Sjohnny /* Save minimal state. */
23770025d76Sjohnny void *npe_statep;
23870025d76Sjohnny 
23970025d76Sjohnny int
_init(void)24070025d76Sjohnny _init(void)
24170025d76Sjohnny {
24270025d76Sjohnny 	int e;
24370025d76Sjohnny 
24470025d76Sjohnny 	/*
24570025d76Sjohnny 	 * Initialize per-pci bus soft state pointer.
24670025d76Sjohnny 	 */
24770025d76Sjohnny 	e = ddi_soft_state_init(&npe_statep, sizeof (pci_state_t), 1);
24870025d76Sjohnny 	if (e != 0)
24970025d76Sjohnny 		return (e);
25070025d76Sjohnny 
25170025d76Sjohnny 	if ((e = mod_install(&modlinkage)) != 0)
25270025d76Sjohnny 		ddi_soft_state_fini(&npe_statep);
25370025d76Sjohnny 
25470025d76Sjohnny 	return (e);
25570025d76Sjohnny }
25670025d76Sjohnny 
25770025d76Sjohnny 
25870025d76Sjohnny int
_fini(void)25970025d76Sjohnny _fini(void)
26070025d76Sjohnny {
26170025d76Sjohnny 	int rc;
26270025d76Sjohnny 
26370025d76Sjohnny 	rc = mod_remove(&modlinkage);
26470025d76Sjohnny 	if (rc != 0)
26570025d76Sjohnny 		return (rc);
26670025d76Sjohnny 
26770025d76Sjohnny 	ddi_soft_state_fini(&npe_statep);
26870025d76Sjohnny 	return (rc);
26970025d76Sjohnny }
27070025d76Sjohnny 
27170025d76Sjohnny 
27270025d76Sjohnny int
_info(struct modinfo * modinfop)27370025d76Sjohnny _info(struct modinfo *modinfop)
27470025d76Sjohnny {
27570025d76Sjohnny 	return (mod_info(&modlinkage, modinfop));
27670025d76Sjohnny }
27770025d76Sjohnny 
27826947304SEvan Yan /*ARGSUSED*/
27926947304SEvan Yan static int
npe_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)28026947304SEvan Yan npe_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
28126947304SEvan Yan {
28226947304SEvan Yan 	minor_t		minor = getminor((dev_t)arg);
28326947304SEvan Yan 	int		instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
28426947304SEvan Yan 	pci_state_t	*pcip = ddi_get_soft_state(npe_statep, instance);
28526947304SEvan Yan 	int		ret = DDI_SUCCESS;
28626947304SEvan Yan 
28726947304SEvan Yan 	switch (cmd) {
28826947304SEvan Yan 	case DDI_INFO_DEVT2INSTANCE:
28926947304SEvan Yan 		*result = (void *)(intptr_t)instance;
29026947304SEvan Yan 		break;
29126947304SEvan Yan 	case DDI_INFO_DEVT2DEVINFO:
29226947304SEvan Yan 		if (pcip == NULL) {
29326947304SEvan Yan 			ret = DDI_FAILURE;
29426947304SEvan Yan 			break;
29526947304SEvan Yan 		}
29626947304SEvan Yan 
29726947304SEvan Yan 		*result = (void *)pcip->pci_dip;
29826947304SEvan Yan 		break;
29926947304SEvan Yan 	default:
30026947304SEvan Yan 		ret = DDI_FAILURE;
30126947304SEvan Yan 		break;
30226947304SEvan Yan 	}
30326947304SEvan Yan 
30426947304SEvan Yan 	return (ret);
30526947304SEvan Yan }
30626947304SEvan Yan 
307ffb64830SJordan Paige Hendricks /*
308ffb64830SJordan Paige Hendricks  * See big theory statement at the top of this file for more information about
309ffb64830SJordan Paige Hendricks  * surprise removal events.
310ffb64830SJordan Paige Hendricks  */
311ffb64830SJordan Paige Hendricks #define	NPE_EVENT_TAG_HOT_REMOVAL	0
312ffb64830SJordan Paige Hendricks static ndi_event_definition_t npe_ndi_event_defs[1] = {
313ffb64830SJordan Paige Hendricks 	{NPE_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL,
314ffb64830SJordan Paige Hendricks 	NDI_EVENT_POST_TO_ALL}
315ffb64830SJordan Paige Hendricks };
316ffb64830SJordan Paige Hendricks 
317ffb64830SJordan Paige Hendricks static ndi_event_set_t npe_ndi_events = {
318ffb64830SJordan Paige Hendricks 	NDI_EVENTS_REV1, ARRAY_SIZE(npe_ndi_event_defs), npe_ndi_event_defs
319ffb64830SJordan Paige Hendricks };
320ffb64830SJordan Paige Hendricks 
32170025d76Sjohnny /*ARGSUSED*/
32270025d76Sjohnny static int
npe_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)32370025d76Sjohnny npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
32470025d76Sjohnny {
32526947304SEvan Yan 	int		instance = ddi_get_instance(devi);
32626947304SEvan Yan 	pci_state_t	*pcip = NULL;
327ffb64830SJordan Paige Hendricks 	int		ret;
32870025d76Sjohnny 
329cb7ea99dSJimmy Vetayases 	if (cmd == DDI_RESUME) {
330cb7ea99dSJimmy Vetayases 		/*
331cb7ea99dSJimmy Vetayases 		 * the system might still be able to resume even if this fails
332cb7ea99dSJimmy Vetayases 		 */
333cb7ea99dSJimmy Vetayases 		(void) npe_restore_htconfig_children(devi);
3342df1fe9cSrandyf 		return (DDI_SUCCESS);
335cb7ea99dSJimmy Vetayases 	}
336cb7ea99dSJimmy Vetayases 
337cb7ea99dSJimmy Vetayases 	/*
338cb7ea99dSJimmy Vetayases 	 * We must do this here in order to ensure that all top level devices
339cb7ea99dSJimmy Vetayases 	 * get their HyperTransport MSI mapping regs programmed first.
340cb7ea99dSJimmy Vetayases 	 * "Memory controller" and "hostbridge" class devices are leaf devices
341cb7ea99dSJimmy Vetayases 	 * that may affect MSI translation functionality for devices
342cb7ea99dSJimmy Vetayases 	 * connected to the same link/bus.
343cb7ea99dSJimmy Vetayases 	 *
344cb7ea99dSJimmy Vetayases 	 * This will also program HT MSI mapping registers on root buses
345cb7ea99dSJimmy Vetayases 	 * devices (basically sitting on an HT bus) that are not dependent
346cb7ea99dSJimmy Vetayases 	 * on the aforementioned HT devices for MSI translation.
347cb7ea99dSJimmy Vetayases 	 */
348cb7ea99dSJimmy Vetayases 	npe_enable_htmsi_children(devi);
3492df1fe9cSrandyf 
35070025d76Sjohnny 	if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, "device_type",
35170025d76Sjohnny 	    "pciex") != DDI_PROP_SUCCESS) {
35270025d76Sjohnny 		cmn_err(CE_WARN, "npe:  'device_type' prop create failed");
35370025d76Sjohnny 	}
35470025d76Sjohnny 
35570025d76Sjohnny 	if (ddi_soft_state_zalloc(npe_statep, instance) == DDI_SUCCESS)
35670025d76Sjohnny 		pcip = ddi_get_soft_state(npe_statep, instance);
35770025d76Sjohnny 
35870025d76Sjohnny 	if (pcip == NULL)
35970025d76Sjohnny 		return (DDI_FAILURE);
36070025d76Sjohnny 
36170025d76Sjohnny 	pcip->pci_dip = devi;
36226947304SEvan Yan 	pcip->pci_soft_state = PCI_SOFT_STATE_CLOSED;
36370025d76Sjohnny 
36426947304SEvan Yan 	if (pcie_init(devi, NULL) != DDI_SUCCESS)
36526947304SEvan Yan 		goto fail1;
36670025d76Sjohnny 
367ffb64830SJordan Paige Hendricks 	ret = ndi_event_alloc_hdl(pcip->pci_dip, NULL, &pcip->pci_ndi_event_hdl,
368ffb64830SJordan Paige Hendricks 	    NDI_SLEEP);
369ffb64830SJordan Paige Hendricks 	if (ret == NDI_SUCCESS) {
370ffb64830SJordan Paige Hendricks 		ret = ndi_event_bind_set(pcip->pci_ndi_event_hdl,
371ffb64830SJordan Paige Hendricks 		    &npe_ndi_events, NDI_SLEEP);
372ffb64830SJordan Paige Hendricks 		if (ret != NDI_SUCCESS) {
373ffb64830SJordan Paige Hendricks 			dev_err(pcip->pci_dip, CE_WARN, "npe:	failed to bind "
374ffb64830SJordan Paige Hendricks 			    "NDI event set (error=%d)", ret);
375ffb64830SJordan Paige Hendricks 			goto fail1;
376ffb64830SJordan Paige Hendricks 		}
377ffb64830SJordan Paige Hendricks 	} else {
378ffb64830SJordan Paige Hendricks 		dev_err(pcip->pci_dip, CE_WARN, "npe:	failed to allocate "
379ffb64830SJordan Paige Hendricks 		    "event handle (error=%d)", ret);
380ffb64830SJordan Paige Hendricks 		goto fail1;
381ffb64830SJordan Paige Hendricks 	}
382ffb64830SJordan Paige Hendricks 
3837a364d25Sschwartz 	/* Second arg: initialize for pci_express root nexus */
38426947304SEvan Yan 	if (pcitool_init(devi, B_TRUE) != DDI_SUCCESS)
38526947304SEvan Yan 		goto fail2;
386eae2e508Skrishnae 
38700d0963fSdilpreet 	pcip->pci_fmcap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE |
38800d0963fSdilpreet 	    DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
38900d0963fSdilpreet 	ddi_fm_init(devi, &pcip->pci_fmcap, &pcip->pci_fm_ibc);
39000d0963fSdilpreet 
391eae2e508Skrishnae 	if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE) {
39200d0963fSdilpreet 		ddi_fm_handler_register(devi, npe_fm_callback, NULL);
393eae2e508Skrishnae 	}
394eae2e508Skrishnae 
395eae2e508Skrishnae 	PCIE_DIP2PFD(devi) = kmem_zalloc(sizeof (pf_data_t), KM_SLEEP);
396eae2e508Skrishnae 	pcie_rc_init_pfd(devi, PCIE_DIP2PFD(devi));
39770025d76Sjohnny 
39870025d76Sjohnny 	ddi_report_dev(devi);
399c0da6274SZhi-Jun Robin Fu 	pcie_fab_init_bus(devi, PCIE_BUS_FINAL);
40026947304SEvan Yan 
40170025d76Sjohnny 	return (DDI_SUCCESS);
40270025d76Sjohnny 
40326947304SEvan Yan fail2:
40426947304SEvan Yan 	(void) pcie_uninit(devi);
40526947304SEvan Yan fail1:
40626947304SEvan Yan 	pcie_rc_fini_bus(devi);
40726947304SEvan Yan 	ddi_soft_state_free(npe_statep, instance);
40826947304SEvan Yan 
40926947304SEvan Yan 	return (DDI_FAILURE);
41070025d76Sjohnny }
41170025d76Sjohnny 
41270025d76Sjohnny /*ARGSUSED*/
41370025d76Sjohnny static int
npe_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)41470025d76Sjohnny npe_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
41570025d76Sjohnny {
41670025d76Sjohnny 	int instance = ddi_get_instance(devi);
41700d0963fSdilpreet 	pci_state_t *pcip;
418ffb64830SJordan Paige Hendricks 	int ret;
41900d0963fSdilpreet 
42000d0963fSdilpreet 	pcip = ddi_get_soft_state(npe_statep, ddi_get_instance(devi));
42170025d76Sjohnny 
4222df1fe9cSrandyf 	switch (cmd) {
4232df1fe9cSrandyf 	case DDI_DETACH:
424ffb64830SJordan Paige Hendricks 
425ffb64830SJordan Paige Hendricks 		/*
426ffb64830SJordan Paige Hendricks 		 * Clean up event handling first, to ensure there are no
427ffb64830SJordan Paige Hendricks 		 * oustanding callbacks registered.
428ffb64830SJordan Paige Hendricks 		 */
429ffb64830SJordan Paige Hendricks 		ret = ndi_event_unbind_set(pcip->pci_ndi_event_hdl,
430ffb64830SJordan Paige Hendricks 		    &npe_ndi_events, NDI_SLEEP);
431ffb64830SJordan Paige Hendricks 		if (ret == NDI_SUCCESS) {
432ffb64830SJordan Paige Hendricks 			/* ndi_event_free_hdl always succeeds. */
433ffb64830SJordan Paige Hendricks 			(void) ndi_event_free_hdl(pcip->pci_ndi_event_hdl);
434ffb64830SJordan Paige Hendricks 		} else {
435ffb64830SJordan Paige Hendricks 			/*
436ffb64830SJordan Paige Hendricks 			 * The event set will only fail to unbind if there are
437ffb64830SJordan Paige Hendricks 			 * outstanding callbacks registered for it, which
438ffb64830SJordan Paige Hendricks 			 * probably means a child driver still has one
439ffb64830SJordan Paige Hendricks 			 * registered and thus was not cleaned up properly
440ffb64830SJordan Paige Hendricks 			 * before npe's detach routine was called. Consequently,
441ffb64830SJordan Paige Hendricks 			 * we should fail the detach here.
442ffb64830SJordan Paige Hendricks 			 */
443ffb64830SJordan Paige Hendricks 			dev_err(pcip->pci_dip, CE_WARN, "npe:	failed to "
444ffb64830SJordan Paige Hendricks 			    "unbind NDI event set (error=%d)", ret);
445ffb64830SJordan Paige Hendricks 			return (DDI_FAILURE);
446ffb64830SJordan Paige Hendricks 		}
447ffb64830SJordan Paige Hendricks 
448c0da6274SZhi-Jun Robin Fu 		pcie_fab_fini_bus(devi, PCIE_BUS_INITIAL);
44970025d76Sjohnny 
4502df1fe9cSrandyf 		/* Uninitialize pcitool support. */
4512df1fe9cSrandyf 		pcitool_uninit(devi);
45200d0963fSdilpreet 
45326947304SEvan Yan 		if (pcie_uninit(devi) != DDI_SUCCESS)
45426947304SEvan Yan 			return (DDI_FAILURE);
45500d0963fSdilpreet 
4562df1fe9cSrandyf 		if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE)
4572df1fe9cSrandyf 			ddi_fm_handler_unregister(devi);
4582df1fe9cSrandyf 
459eae2e508Skrishnae 		pcie_rc_fini_pfd(PCIE_DIP2PFD(devi));
460eae2e508Skrishnae 		kmem_free(PCIE_DIP2PFD(devi), sizeof (pf_data_t));
461eae2e508Skrishnae 
4622df1fe9cSrandyf 		ddi_fm_fini(devi);
4632df1fe9cSrandyf 		ddi_soft_state_free(npe_statep, instance);
464ffb64830SJordan Paige Hendricks 
4652df1fe9cSrandyf 		return (DDI_SUCCESS);
4662df1fe9cSrandyf 
4672df1fe9cSrandyf 	case DDI_SUSPEND:
468cb7ea99dSJimmy Vetayases 		/*
469cb7ea99dSJimmy Vetayases 		 * the system might still be able to suspend/resume even if
470cb7ea99dSJimmy Vetayases 		 * this fails
471cb7ea99dSJimmy Vetayases 		 */
472cb7ea99dSJimmy Vetayases 		(void) npe_save_htconfig_children(devi);
4732df1fe9cSrandyf 		return (DDI_SUCCESS);
4742df1fe9cSrandyf 	default:
4752df1fe9cSrandyf 		return (DDI_FAILURE);
4762df1fe9cSrandyf 	}
47770025d76Sjohnny }
47870025d76Sjohnny 
47914f1dfe8SSeth Goldberg /*
48014f1dfe8SSeth Goldberg  * Configure the access handle for standard configuration space
48114f1dfe8SSeth Goldberg  * access (see pci_fm_acc_setup for code that initializes the
48214f1dfe8SSeth Goldberg  * access-function pointers).
48314f1dfe8SSeth Goldberg  */
48414f1dfe8SSeth Goldberg static int
npe_setup_std_pcicfg_acc(dev_info_t * rdip,ddi_map_req_t * mp,ddi_acc_hdl_t * hp,off_t offset,off_t len)48514f1dfe8SSeth Goldberg npe_setup_std_pcicfg_acc(dev_info_t *rdip, ddi_map_req_t *mp,
48614f1dfe8SSeth Goldberg     ddi_acc_hdl_t *hp, off_t offset, off_t len)
48714f1dfe8SSeth Goldberg {
48814f1dfe8SSeth Goldberg 	int ret;
48914f1dfe8SSeth Goldberg 
49014f1dfe8SSeth Goldberg 	if ((ret = pci_fm_acc_setup(hp, offset, len)) ==
49114f1dfe8SSeth Goldberg 	    DDI_SUCCESS) {
49214f1dfe8SSeth Goldberg 		if (DDI_FM_ACC_ERR_CAP(ddi_fm_capable(rdip)) &&
49314f1dfe8SSeth Goldberg 		    mp->map_handlep->ah_acc.devacc_attr_access
49414f1dfe8SSeth Goldberg 		    != DDI_DEFAULT_ACC) {
49514f1dfe8SSeth Goldberg 			ndi_fmc_insert(rdip, ACC_HANDLE,
49614f1dfe8SSeth Goldberg 			    (void *)mp->map_handlep, NULL);
49714f1dfe8SSeth Goldberg 		}
49814f1dfe8SSeth Goldberg 	}
49914f1dfe8SSeth Goldberg 	return (ret);
50014f1dfe8SSeth Goldberg }
50114f1dfe8SSeth Goldberg 
50270025d76Sjohnny static int
npe_bus_map(dev_info_t * dip,dev_info_t * rdip,ddi_map_req_t * mp,off_t offset,off_t len,caddr_t * vaddrp)50370025d76Sjohnny npe_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
50470025d76Sjohnny     off_t offset, off_t len, caddr_t *vaddrp)
50570025d76Sjohnny {
506ffb64830SJordan Paige Hendricks 	int		rnumber;
50770025d76Sjohnny 	int		space;
50800d0963fSdilpreet 	ddi_acc_impl_t	*ap;
50970025d76Sjohnny 	ddi_acc_hdl_t	*hp;
51070025d76Sjohnny 	ddi_map_req_t	mr;
51170025d76Sjohnny 	pci_regspec_t	pci_reg;
51270025d76Sjohnny 	pci_regspec_t	*pci_rp;
5131f0c5e61SRobert Mustacchi 	struct regspec64 reg;
51470025d76Sjohnny 	pci_acc_cfblk_t	*cfp;
51500d0963fSdilpreet 	int		retval;
5165c59319bSDan Mick 	int64_t		*ecfginfo;
5175c59319bSDan Mick 	uint_t		nelem;
5181f0c5e61SRobert Mustacchi 	uint64_t	pci_rlength;
51970025d76Sjohnny 
52070025d76Sjohnny 	mr = *mp; /* Get private copy of request */
52170025d76Sjohnny 	mp = &mr;
52270025d76Sjohnny 
52370025d76Sjohnny 	/*
52470025d76Sjohnny 	 * check for register number
52570025d76Sjohnny 	 */
52670025d76Sjohnny 	switch (mp->map_type) {
52770025d76Sjohnny 	case DDI_MT_REGSPEC:
52870025d76Sjohnny 		pci_reg = *(pci_regspec_t *)(mp->map_obj.rp);
52970025d76Sjohnny 		pci_rp = &pci_reg;
53070025d76Sjohnny 		if (pci_common_get_reg_prop(rdip, pci_rp) != DDI_SUCCESS)
53170025d76Sjohnny 			return (DDI_FAILURE);
53270025d76Sjohnny 		break;
53370025d76Sjohnny 	case DDI_MT_RNUMBER:
53470025d76Sjohnny 		rnumber = mp->map_obj.rnumber;
53570025d76Sjohnny 		/*
53670025d76Sjohnny 		 * get ALL "reg" properties for dip, select the one of
53770025d76Sjohnny 		 * of interest. In x86, "assigned-addresses" property
53870025d76Sjohnny 		 * is identical to the "reg" property, so there is no
53970025d76Sjohnny 		 * need to cross check the two to determine the physical
54070025d76Sjohnny 		 * address of the registers.
54170025d76Sjohnny 		 * This routine still performs some validity checks to
54270025d76Sjohnny 		 * make sure that everything is okay.
54370025d76Sjohnny 		 */
54470025d76Sjohnny 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
5451f0c5e61SRobert Mustacchi 		    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &nelem) !=
5461f0c5e61SRobert Mustacchi 		    DDI_PROP_SUCCESS)
54770025d76Sjohnny 			return (DDI_FAILURE);
54870025d76Sjohnny 
54970025d76Sjohnny 		/*
55070025d76Sjohnny 		 * validate the register number.
55170025d76Sjohnny 		 */
5521f0c5e61SRobert Mustacchi 		nelem /= (sizeof (pci_regspec_t) / sizeof (int));
5531f0c5e61SRobert Mustacchi 		if (rnumber >= nelem) {
55470025d76Sjohnny 			ddi_prop_free(pci_rp);
55570025d76Sjohnny 			return (DDI_FAILURE);
55670025d76Sjohnny 		}
55770025d76Sjohnny 
55870025d76Sjohnny 		/*
55970025d76Sjohnny 		 * copy the required entry.
56070025d76Sjohnny 		 */
56170025d76Sjohnny 		pci_reg = pci_rp[rnumber];
56270025d76Sjohnny 
56370025d76Sjohnny 		/*
56470025d76Sjohnny 		 * free the memory allocated by ddi_prop_lookup_int_array
56570025d76Sjohnny 		 */
56670025d76Sjohnny 		ddi_prop_free(pci_rp);
56770025d76Sjohnny 
56870025d76Sjohnny 		pci_rp = &pci_reg;
56970025d76Sjohnny 		if (pci_common_get_reg_prop(rdip, pci_rp) != DDI_SUCCESS)
57070025d76Sjohnny 			return (DDI_FAILURE);
57170025d76Sjohnny 		mp->map_type = DDI_MT_REGSPEC;
57270025d76Sjohnny 		break;
57370025d76Sjohnny 	default:
57470025d76Sjohnny 		return (DDI_ME_INVAL);
57570025d76Sjohnny 	}
57670025d76Sjohnny 
57770025d76Sjohnny 	space = pci_rp->pci_phys_hi & PCI_REG_ADDR_M;
57870025d76Sjohnny 
57970025d76Sjohnny 	/*
58070025d76Sjohnny 	 * check for unmap and unlock of address space
58170025d76Sjohnny 	 */
58270025d76Sjohnny 	if ((mp->map_op == DDI_MO_UNMAP) || (mp->map_op == DDI_MO_UNLOCK)) {
58370025d76Sjohnny 		switch (space) {
58470025d76Sjohnny 		case PCI_ADDR_IO:
58570025d76Sjohnny 			reg.regspec_bustype = 1;
58670025d76Sjohnny 			break;
58770025d76Sjohnny 
58870025d76Sjohnny 		case PCI_ADDR_CONFIG:
58914f1dfe8SSeth Goldberg 			/*
59014f1dfe8SSeth Goldberg 			 * If this is an unmap/unlock of a standard config
59114f1dfe8SSeth Goldberg 			 * space mapping (memory-mapped config space mappings
59214f1dfe8SSeth Goldberg 			 * would have the DDI_ACCATTR_CPU_VADDR bit set in the
59314f1dfe8SSeth Goldberg 			 * acc_attr), undo that setup here.
59414f1dfe8SSeth Goldberg 			 */
59514f1dfe8SSeth Goldberg 			if (NPE_IS_HANDLE_FOR_STDCFG_ACC(mp->map_handlep)) {
59614f1dfe8SSeth Goldberg 
59700d0963fSdilpreet 				if (DDI_FM_ACC_ERR_CAP(ddi_fm_capable(rdip)) &&
59800d0963fSdilpreet 				    mp->map_handlep->ah_acc.devacc_attr_access
59900d0963fSdilpreet 				    != DDI_DEFAULT_ACC) {
60000d0963fSdilpreet 					ndi_fmc_remove(rdip, ACC_HANDLE,
60100d0963fSdilpreet 					    (void *)mp->map_handlep);
60200d0963fSdilpreet 				}
603247980beSanish 				return (DDI_SUCCESS);
60400d0963fSdilpreet 			}
60500d0963fSdilpreet 
606*fb876f96SRobert Mustacchi 			pci_rp->pci_size_hi = 0;
60749fbdd30SErwin T Tsaur 			pci_rp->pci_size_low = PCIE_CONF_HDR_SIZE;
608247980beSanish 
609649d4cceSanish 			/* FALLTHROUGH */
61070025d76Sjohnny 		case PCI_ADDR_MEM64:
61170025d76Sjohnny 		case PCI_ADDR_MEM32:
61270025d76Sjohnny 			reg.regspec_bustype = 0;
61370025d76Sjohnny 			break;
61470025d76Sjohnny 
61570025d76Sjohnny 		default:
61670025d76Sjohnny 			return (DDI_FAILURE);
61770025d76Sjohnny 		}
61800d0963fSdilpreet 
6191f0c5e61SRobert Mustacchi 		reg.regspec_addr = (uint64_t)pci_rp->pci_phys_mid << 32 |
6201f0c5e61SRobert Mustacchi 		    (uint64_t)pci_rp->pci_phys_low;
6211f0c5e61SRobert Mustacchi 		reg.regspec_size = (uint64_t)pci_rp->pci_size_hi << 32 |
6221f0c5e61SRobert Mustacchi 		    (uint64_t)pci_rp->pci_size_low;
6231f0c5e61SRobert Mustacchi 
62400d0963fSdilpreet 		/*
62500d0963fSdilpreet 		 * Adjust offset and length
62600d0963fSdilpreet 		 * A non-zero length means override the one in the regspec.
62700d0963fSdilpreet 		 */
6281f0c5e61SRobert Mustacchi 		if (reg.regspec_addr + offset < MAX(reg.regspec_addr, offset))
6291f0c5e61SRobert Mustacchi 			return (DDI_FAILURE);
6301f0c5e61SRobert Mustacchi 		reg.regspec_addr += offset;
63100d0963fSdilpreet 		if (len != 0)
6321f0c5e61SRobert Mustacchi 			reg.regspec_size = len;
63370025d76Sjohnny 
6341f0c5e61SRobert Mustacchi 		mp->map_obj.rp = (struct regspec *)&reg;
6351f0c5e61SRobert Mustacchi 		mp->map_flags |= DDI_MF_EXT_REGSPEC;
63600d0963fSdilpreet 		retval = ddi_map(dip, mp, (off_t)0, (off_t)0, vaddrp);
63700d0963fSdilpreet 		if (DDI_FM_ACC_ERR_CAP(ddi_fm_capable(rdip)) &&
63800d0963fSdilpreet 		    mp->map_handlep->ah_acc.devacc_attr_access !=
63900d0963fSdilpreet 		    DDI_DEFAULT_ACC) {
64000d0963fSdilpreet 			ndi_fmc_remove(rdip, ACC_HANDLE,
64100d0963fSdilpreet 			    (void *)mp->map_handlep);
64200d0963fSdilpreet 		}
64300d0963fSdilpreet 		return (retval);
64470025d76Sjohnny 
64570025d76Sjohnny 	}
64670025d76Sjohnny 
64770025d76Sjohnny 	/* check for user mapping request - not legal for Config */
64870025d76Sjohnny 	if (mp->map_op == DDI_MO_MAP_HANDLE && space == PCI_ADDR_CONFIG) {
64970025d76Sjohnny 		cmn_err(CE_NOTE, "npe: Config mapping request from user\n");
65070025d76Sjohnny 		return (DDI_FAILURE);
65170025d76Sjohnny 	}
65270025d76Sjohnny 
65370025d76Sjohnny 
65400d0963fSdilpreet 	/*
65500d0963fSdilpreet 	 * Note that pci_fm_acc_setup() is called to serve two purposes
65600d0963fSdilpreet 	 * i) enable legacy PCI I/O style config space access
65700d0963fSdilpreet 	 * ii) register with FMA
65800d0963fSdilpreet 	 */
65970025d76Sjohnny 	if (space == PCI_ADDR_CONFIG) {
66014f1dfe8SSeth Goldberg 
66170025d76Sjohnny 		/* Can't map config space without a handle */
66270025d76Sjohnny 		hp = (ddi_acc_hdl_t *)mp->map_handlep;
66370025d76Sjohnny 		if (hp == NULL)
66470025d76Sjohnny 			return (DDI_FAILURE);
66570025d76Sjohnny 
66670025d76Sjohnny 		/* record the device address for future reference */
66770025d76Sjohnny 		cfp = (pci_acc_cfblk_t *)&hp->ah_bus_private;
66870025d76Sjohnny 		cfp->c_busnum = PCI_REG_BUS_G(pci_rp->pci_phys_hi);
66970025d76Sjohnny 		cfp->c_devnum = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
67070025d76Sjohnny 		cfp->c_funcnum = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
67170025d76Sjohnny 
67200d0963fSdilpreet 		*vaddrp = (caddr_t)offset;
673649d4cceSanish 
67449fbdd30SErwin T Tsaur 		/* Check if MMCFG is supported */
67514f1dfe8SSeth Goldberg 		if (!npe_is_mmcfg_supported(rdip)) {
67614f1dfe8SSeth Goldberg 			return (npe_setup_std_pcicfg_acc(rdip, mp, hp,
67714f1dfe8SSeth Goldberg 			    offset, len));
67800d0963fSdilpreet 		}
679247980beSanish 
68014f1dfe8SSeth Goldberg 
6815c59319bSDan Mick 		if (ddi_prop_lookup_int64_array(DDI_DEV_T_ANY, rdip, 0,
6825c59319bSDan Mick 		    "ecfg", &ecfginfo, &nelem) == DDI_PROP_SUCCESS) {
683649d4cceSanish 
68414f1dfe8SSeth Goldberg 			if (nelem != 4 ||
68514f1dfe8SSeth Goldberg 			    cfp->c_busnum < ecfginfo[2] ||
6865c59319bSDan Mick 			    cfp->c_busnum > ecfginfo[3]) {
68714f1dfe8SSeth Goldberg 				/*
68814f1dfe8SSeth Goldberg 				 * Invalid property or Doesn't contain the
68914f1dfe8SSeth Goldberg 				 * requested bus; fall back to standard
69014f1dfe8SSeth Goldberg 				 * (I/O-based) config access.
69114f1dfe8SSeth Goldberg 				 */
6925c59319bSDan Mick 				ddi_prop_free(ecfginfo);
69314f1dfe8SSeth Goldberg 				return (npe_setup_std_pcicfg_acc(rdip, mp, hp,
69414f1dfe8SSeth Goldberg 				    offset, len));
69514f1dfe8SSeth Goldberg 			} else {
696*fb876f96SRobert Mustacchi 				uint64_t addr = (uint64_t)ecfginfo[0];
697*fb876f96SRobert Mustacchi 
698*fb876f96SRobert Mustacchi 				/*
699*fb876f96SRobert Mustacchi 				 * The address for memory mapped configuration
700*fb876f96SRobert Mustacchi 				 * space may theoretically be anywhere in the
701*fb876f96SRobert Mustacchi 				 * processor's physical address space.
702*fb876f96SRobert Mustacchi 				 *
703*fb876f96SRobert Mustacchi 				 * We need to set both phys_mid and phys_low to
704*fb876f96SRobert Mustacchi 				 * account for this. Because we are mapping a
705*fb876f96SRobert Mustacchi 				 * single device, which has 1 KiB region and
706*fb876f96SRobert Mustacchi 				 * alignment requirements, along with the fact
707*fb876f96SRobert Mustacchi 				 * that we only allow for segment 0, means that
708*fb876f96SRobert Mustacchi 				 * the offset will always fit in the lower
709*fb876f96SRobert Mustacchi 				 * 32-bit word.
710*fb876f96SRobert Mustacchi 				 */
711*fb876f96SRobert Mustacchi 				pci_rp->pci_phys_mid = (uint32_t)(addr >> 32);
712*fb876f96SRobert Mustacchi 				pci_rp->pci_phys_low = (uint32_t)addr;
7135c59319bSDan Mick 
71414f1dfe8SSeth Goldberg 				ddi_prop_free(ecfginfo);
71570025d76Sjohnny 
71614f1dfe8SSeth Goldberg 				pci_rp->pci_phys_low += ((cfp->c_busnum << 20) |
71714f1dfe8SSeth Goldberg 				    (cfp->c_devnum) << 15 |
71814f1dfe8SSeth Goldberg 				    (cfp->c_funcnum << 12));
7195c59319bSDan Mick 
720*fb876f96SRobert Mustacchi 				pci_rp->pci_size_hi = 0;
72114f1dfe8SSeth Goldberg 				pci_rp->pci_size_low = PCIE_CONF_HDR_SIZE;
72214f1dfe8SSeth Goldberg 			}
7235c59319bSDan Mick 		} else {
72414f1dfe8SSeth Goldberg 			/*
72514f1dfe8SSeth Goldberg 			 * Couldn't find the MMCFG property -- fall back to
72614f1dfe8SSeth Goldberg 			 * standard config access
72714f1dfe8SSeth Goldberg 			 */
72814f1dfe8SSeth Goldberg 			return (npe_setup_std_pcicfg_acc(rdip, mp, hp,
72914f1dfe8SSeth Goldberg 			    offset, len));
7305c59319bSDan Mick 		}
73170025d76Sjohnny 	}
73270025d76Sjohnny 
73370025d76Sjohnny 	/*
73470025d76Sjohnny 	 * range check
73570025d76Sjohnny 	 */
7361f0c5e61SRobert Mustacchi 	pci_rlength = (uint64_t)pci_rp->pci_size_low |
7371f0c5e61SRobert Mustacchi 	    (uint64_t)pci_rp->pci_size_hi << 32;
7381f0c5e61SRobert Mustacchi 	if ((offset >= pci_rlength) || (len > pci_rlength) ||
7391f0c5e61SRobert Mustacchi 	    (offset + len > pci_rlength) || (offset + len < MAX(offset, len))) {
74070025d76Sjohnny 		return (DDI_FAILURE);
7411f0c5e61SRobert Mustacchi 	}
74270025d76Sjohnny 
74370025d76Sjohnny 	/*
74470025d76Sjohnny 	 * convert the pci regsec into the generic regspec used by the
74570025d76Sjohnny 	 * parent root nexus driver.
74670025d76Sjohnny 	 */
74770025d76Sjohnny 	switch (space) {
74870025d76Sjohnny 	case PCI_ADDR_IO:
74970025d76Sjohnny 		reg.regspec_bustype = 1;
75070025d76Sjohnny 		break;
75170025d76Sjohnny 	case PCI_ADDR_CONFIG:
75270025d76Sjohnny 	case PCI_ADDR_MEM64:
75370025d76Sjohnny 	case PCI_ADDR_MEM32:
75470025d76Sjohnny 		reg.regspec_bustype = 0;
75570025d76Sjohnny 		break;
75670025d76Sjohnny 	default:
75770025d76Sjohnny 		return (DDI_FAILURE);
75870025d76Sjohnny 	}
75970025d76Sjohnny 
7601f0c5e61SRobert Mustacchi 	reg.regspec_addr = (uint64_t)pci_rp->pci_phys_mid << 32 |
7611f0c5e61SRobert Mustacchi 	    (uint64_t)pci_rp->pci_phys_low;
7621f0c5e61SRobert Mustacchi 	reg.regspec_size = pci_rlength;
76370025d76Sjohnny 
7641f0c5e61SRobert Mustacchi 	/*
7651f0c5e61SRobert Mustacchi 	 * Adjust offset and length
7661f0c5e61SRobert Mustacchi 	 * A non-zero length means override the one in the regspec.
7671f0c5e61SRobert Mustacchi 	 */
7681f0c5e61SRobert Mustacchi 	if (reg.regspec_addr + offset < MAX(reg.regspec_addr, offset))
7691f0c5e61SRobert Mustacchi 		return (DDI_FAILURE);
7701f0c5e61SRobert Mustacchi 	reg.regspec_addr += offset;
7711f0c5e61SRobert Mustacchi 	if (len != 0)
7721f0c5e61SRobert Mustacchi 		reg.regspec_size = len;
7731f0c5e61SRobert Mustacchi 
7741f0c5e61SRobert Mustacchi 
7751f0c5e61SRobert Mustacchi 	mp->map_obj.rp = (struct regspec *)&reg;
7761f0c5e61SRobert Mustacchi 	mp->map_flags |= DDI_MF_EXT_REGSPEC;
77700d0963fSdilpreet 	retval = ddi_map(dip, mp, (off_t)0, (off_t)0, vaddrp);
77800d0963fSdilpreet 	if (retval == DDI_SUCCESS) {
77900d0963fSdilpreet 		/*
78000d0963fSdilpreet 		 * For config space gets force use of cautious access routines.
78100d0963fSdilpreet 		 * These will handle default and protected mode accesses too.
78200d0963fSdilpreet 		 */
78300d0963fSdilpreet 		if (space == PCI_ADDR_CONFIG) {
78400d0963fSdilpreet 			ap = (ddi_acc_impl_t *)mp->map_handlep;
78500d0963fSdilpreet 			ap->ahi_acc_attr &= ~DDI_ACCATTR_DIRECT;
78600d0963fSdilpreet 			ap->ahi_acc_attr |= DDI_ACCATTR_CONFIG_SPACE;
78700d0963fSdilpreet 			ap->ahi_get8 = i_ddi_caut_get8;
78800d0963fSdilpreet 			ap->ahi_get16 = i_ddi_caut_get16;
78900d0963fSdilpreet 			ap->ahi_get32 = i_ddi_caut_get32;
79000d0963fSdilpreet 			ap->ahi_get64 = i_ddi_caut_get64;
79100d0963fSdilpreet 			ap->ahi_rep_get8 = i_ddi_caut_rep_get8;
79200d0963fSdilpreet 			ap->ahi_rep_get16 = i_ddi_caut_rep_get16;
79300d0963fSdilpreet 			ap->ahi_rep_get32 = i_ddi_caut_rep_get32;
79400d0963fSdilpreet 			ap->ahi_rep_get64 = i_ddi_caut_rep_get64;
79500d0963fSdilpreet 		}
79600d0963fSdilpreet 		if (DDI_FM_ACC_ERR_CAP(ddi_fm_capable(rdip)) &&
79700d0963fSdilpreet 		    mp->map_handlep->ah_acc.devacc_attr_access !=
79800d0963fSdilpreet 		    DDI_DEFAULT_ACC) {
79900d0963fSdilpreet 			ndi_fmc_insert(rdip, ACC_HANDLE,
80000d0963fSdilpreet 			    (void *)mp->map_handlep, NULL);
80100d0963fSdilpreet 		}
80200d0963fSdilpreet 	}
80300d0963fSdilpreet 	return (retval);
80470025d76Sjohnny }
80570025d76Sjohnny 
80670025d76Sjohnny 
807649d4cceSanish 
80870025d76Sjohnny /*ARGSUSED*/
80970025d76Sjohnny static int
npe_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)81070025d76Sjohnny npe_ctlops(dev_info_t *dip, dev_info_t *rdip,
8111f0c5e61SRobert Mustacchi     ddi_ctl_enum_t ctlop, void *arg, void *result)
81270025d76Sjohnny {
81370025d76Sjohnny 	int		totreg;
81470025d76Sjohnny 	uint_t		reglen;
81570025d76Sjohnny 	pci_regspec_t	*drv_regp;
816eae2e508Skrishnae 	struct attachspec *asp;
817eae2e508Skrishnae 	struct detachspec *dsp;
818eae2e508Skrishnae 	pci_state_t	*pci_p = ddi_get_soft_state(npe_statep,
819eae2e508Skrishnae 	    ddi_get_instance(dip));
82070025d76Sjohnny 
82170025d76Sjohnny 	switch (ctlop) {
82270025d76Sjohnny 	case DDI_CTLOPS_REPORTDEV:
82370025d76Sjohnny 		if (rdip == (dev_info_t *)0)
82470025d76Sjohnny 			return (DDI_FAILURE);
82570025d76Sjohnny 		cmn_err(CE_CONT, "?PCI Express-device: %s@%s, %s%d\n",
82670025d76Sjohnny 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
82770025d76Sjohnny 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
82870025d76Sjohnny 		return (DDI_SUCCESS);
82970025d76Sjohnny 
83070025d76Sjohnny 	case DDI_CTLOPS_INITCHILD:
83170025d76Sjohnny 		return (npe_initchild((dev_info_t *)arg));
83270025d76Sjohnny 
83370025d76Sjohnny 	case DDI_CTLOPS_UNINITCHILD:
83470025d76Sjohnny 		return (npe_removechild((dev_info_t *)arg));
83570025d76Sjohnny 
83670025d76Sjohnny 	case DDI_CTLOPS_SIDDEV:
83770025d76Sjohnny 		return (DDI_SUCCESS);
83870025d76Sjohnny 
83970025d76Sjohnny 	case DDI_CTLOPS_REGSIZE:
84070025d76Sjohnny 	case DDI_CTLOPS_NREGS:
84170025d76Sjohnny 		if (rdip == (dev_info_t *)0)
84270025d76Sjohnny 			return (DDI_FAILURE);
84370025d76Sjohnny 
84470025d76Sjohnny 		*(int *)result = 0;
84570025d76Sjohnny 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
84670025d76Sjohnny 		    DDI_PROP_DONTPASS, "reg", (int **)&drv_regp,
84770025d76Sjohnny 		    &reglen) != DDI_PROP_SUCCESS) {
84870025d76Sjohnny 			return (DDI_FAILURE);
84970025d76Sjohnny 		}
85070025d76Sjohnny 
85170025d76Sjohnny 		totreg = (reglen * sizeof (int)) / sizeof (pci_regspec_t);
85270025d76Sjohnny 		if (ctlop == DDI_CTLOPS_NREGS)
85370025d76Sjohnny 			*(int *)result = totreg;
85470025d76Sjohnny 		else if (ctlop == DDI_CTLOPS_REGSIZE) {
8551f0c5e61SRobert Mustacchi 			uint64_t val;
8561f0c5e61SRobert Mustacchi 			int rn;
8571f0c5e61SRobert Mustacchi 
85870025d76Sjohnny 			rn = *(int *)arg;
85970025d76Sjohnny 			if (rn >= totreg) {
86070025d76Sjohnny 				ddi_prop_free(drv_regp);
86170025d76Sjohnny 				return (DDI_FAILURE);
86270025d76Sjohnny 			}
8631f0c5e61SRobert Mustacchi 			val = drv_regp[rn].pci_size_low |
8641f0c5e61SRobert Mustacchi 			    (uint64_t)drv_regp[rn].pci_size_hi << 32;
8651f0c5e61SRobert Mustacchi 			if (val > OFF_MAX) {
8661f0c5e61SRobert Mustacchi 				int ce = CE_NOTE;
8671f0c5e61SRobert Mustacchi #ifdef DEBUG
8681f0c5e61SRobert Mustacchi 				ce = CE_WARN;
8691f0c5e61SRobert Mustacchi #endif
8701f0c5e61SRobert Mustacchi 				dev_err(rdip, ce, "failed to get register "
8711f0c5e61SRobert Mustacchi 				    "size, value larger than OFF_MAX: 0x%"
8721f0c5e61SRobert Mustacchi 				    PRIx64 "\n", val);
8731f0c5e61SRobert Mustacchi 				return (DDI_FAILURE);
8741f0c5e61SRobert Mustacchi 			}
8751f0c5e61SRobert Mustacchi 			*(off_t *)result = (off_t)val;
87670025d76Sjohnny 		}
87770025d76Sjohnny 		ddi_prop_free(drv_regp);
87870025d76Sjohnny 
87970025d76Sjohnny 		return (DDI_SUCCESS);
88070025d76Sjohnny 
88170025d76Sjohnny 	case DDI_CTLOPS_POWER:
88270025d76Sjohnny 	{
88370025d76Sjohnny 		power_req_t	*reqp = (power_req_t *)arg;
88470025d76Sjohnny 		/*
88570025d76Sjohnny 		 * We currently understand reporting of PCI_PM_IDLESPEED
88670025d76Sjohnny 		 * capability. Everything else is passed up.
88770025d76Sjohnny 		 */
88870025d76Sjohnny 		if ((reqp->request_type == PMR_REPORT_PMCAP) &&
88970025d76Sjohnny 		    (reqp->req.report_pmcap_req.cap ==  PCI_PM_IDLESPEED))
89070025d76Sjohnny 			return (DDI_SUCCESS);
89170025d76Sjohnny 
89270025d76Sjohnny 		break;
89370025d76Sjohnny 	}
89470025d76Sjohnny 
89500d0963fSdilpreet 	case DDI_CTLOPS_PEEK:
89600d0963fSdilpreet 	case DDI_CTLOPS_POKE:
89700d0963fSdilpreet 		return (pci_common_peekpoke(dip, rdip, ctlop, arg, result));
89800d0963fSdilpreet 
8992df1fe9cSrandyf 	/* X86 systems support PME wakeup from suspended state */
9002df1fe9cSrandyf 	case DDI_CTLOPS_ATTACH:
901eae2e508Skrishnae 		if (!pcie_is_child(dip, rdip))
902eae2e508Skrishnae 			return (DDI_SUCCESS);
903eae2e508Skrishnae 
9042df1fe9cSrandyf 		asp = (struct attachspec *)arg;
905eae2e508Skrishnae 		if ((asp->when == DDI_POST) && (asp->result == DDI_SUCCESS)) {
906eae2e508Skrishnae 			pf_init(rdip, (void *)pci_p->pci_fm_ibc, asp->cmd);
907eae2e508Skrishnae 			(void) pcie_postattach_child(rdip);
908eae2e508Skrishnae 		}
909eae2e508Skrishnae 
9102df1fe9cSrandyf 		/* only do this for immediate children */
9112df1fe9cSrandyf 		if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE &&
9122df1fe9cSrandyf 		    ddi_get_parent(rdip) == dip)
9132df1fe9cSrandyf 			if (pci_pre_resume(rdip) != DDI_SUCCESS) {
9142df1fe9cSrandyf 				/* Not good, better stop now. */
9152df1fe9cSrandyf 				cmn_err(CE_PANIC,
9162df1fe9cSrandyf 				    "Couldn't pre-resume device %p",
9172df1fe9cSrandyf 				    (void *) dip);
9182df1fe9cSrandyf 				/* NOTREACHED */
9192df1fe9cSrandyf 			}
920eae2e508Skrishnae 
921eae2e508Skrishnae 		return (DDI_SUCCESS);
9222df1fe9cSrandyf 
9232df1fe9cSrandyf 	case DDI_CTLOPS_DETACH:
924eae2e508Skrishnae 		if (!pcie_is_child(dip, rdip))
925eae2e508Skrishnae 			return (DDI_SUCCESS);
926eae2e508Skrishnae 
927eae2e508Skrishnae 		dsp = (struct detachspec *)arg;
928eae2e508Skrishnae 
929eae2e508Skrishnae 		if (dsp->when == DDI_PRE)
930eae2e508Skrishnae 			pf_fini(rdip, dsp->cmd);
931eae2e508Skrishnae 
9322df1fe9cSrandyf 		/* only do this for immediate children */
933eae2e508Skrishnae 		if (dsp->cmd == DDI_SUSPEND && dsp->when == DDI_POST &&
9342df1fe9cSrandyf 		    ddi_get_parent(rdip) == dip)
9352df1fe9cSrandyf 			if (pci_post_suspend(rdip) != DDI_SUCCESS)
9362df1fe9cSrandyf 				return (DDI_FAILURE);
937eae2e508Skrishnae 
938eae2e508Skrishnae 		return (DDI_SUCCESS);
9392df1fe9cSrandyf 
94070025d76Sjohnny 	default:
94170025d76Sjohnny 		break;
94270025d76Sjohnny 	}
94370025d76Sjohnny 
94470025d76Sjohnny 	return (ddi_ctlops(dip, rdip, ctlop, arg, result));
94570025d76Sjohnny 
94670025d76Sjohnny }
94770025d76Sjohnny 
94870025d76Sjohnny 
94970025d76Sjohnny /*
95070025d76Sjohnny  * npe_intr_ops
95170025d76Sjohnny  */
95270025d76Sjohnny static int
npe_intr_ops(dev_info_t * pdip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)95370025d76Sjohnny npe_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
95470025d76Sjohnny     ddi_intr_handle_impl_t *hdlp, void *result)
95570025d76Sjohnny {
95670025d76Sjohnny 	return (pci_common_intr_ops(pdip, rdip, intr_op, hdlp, result));
95770025d76Sjohnny }
95870025d76Sjohnny 
95970025d76Sjohnny 
96070025d76Sjohnny static int
npe_initchild(dev_info_t * child)96170025d76Sjohnny npe_initchild(dev_info_t *child)
96270025d76Sjohnny {
963eae2e508Skrishnae 	char		name[80];
964eae2e508Skrishnae 	pcie_bus_t	*bus_p;
965eae2e508Skrishnae 	uint32_t	regs;
966337fc9e2Sanish 	ddi_acc_handle_t	cfg_hdl;
96770025d76Sjohnny 
9687a23d100Sanish 	/*
9697a23d100Sanish 	 * Do not bind drivers to empty bridges.
9707a23d100Sanish 	 * Fail above, if the bridge is found to be hotplug capable
9717a23d100Sanish 	 */
9727a23d100Sanish 	if (npe_disable_empty_bridges_workaround(child) == 1)
9737a23d100Sanish 		return (DDI_FAILURE);
9747a23d100Sanish 
97570025d76Sjohnny 	if (pci_common_name_child(child, name, 80) != DDI_SUCCESS)
97670025d76Sjohnny 		return (DDI_FAILURE);
97770025d76Sjohnny 
97870025d76Sjohnny 	ddi_set_name_addr(child, name);
97970025d76Sjohnny 
98070025d76Sjohnny 	/*
98170025d76Sjohnny 	 * Pseudo nodes indicate a prototype node with per-instance
98270025d76Sjohnny 	 * properties to be merged into the real h/w device node.
98370025d76Sjohnny 	 * The interpretation of the unit-address is DD[,F]
98470025d76Sjohnny 	 * where DD is the device id and F is the function.
98570025d76Sjohnny 	 */
98670025d76Sjohnny 	if (ndi_dev_is_persistent_node(child) == 0) {
98770025d76Sjohnny 		extern int pci_allow_pseudo_children;
98870025d76Sjohnny 
98970025d76Sjohnny 		ddi_set_parent_data(child, NULL);
99070025d76Sjohnny 
99170025d76Sjohnny 		/*
99270025d76Sjohnny 		 * Try to merge the properties from this prototype
99370025d76Sjohnny 		 * node into real h/w nodes.
99470025d76Sjohnny 		 */
99570025d76Sjohnny 		if (ndi_merge_node(child, pci_common_name_child) ==
99670025d76Sjohnny 		    DDI_SUCCESS) {
99770025d76Sjohnny 			/*
99870025d76Sjohnny 			 * Merged ok - return failure to remove the node.
99970025d76Sjohnny 			 */
100070025d76Sjohnny 			ddi_set_name_addr(child, NULL);
100170025d76Sjohnny 			return (DDI_FAILURE);
100270025d76Sjohnny 		}
100370025d76Sjohnny 
100470025d76Sjohnny 		/* workaround for DDIVS to run under PCI Express */
100570025d76Sjohnny 		if (pci_allow_pseudo_children) {
100670025d76Sjohnny 			/*
100770025d76Sjohnny 			 * If the "interrupts" property doesn't exist,
100870025d76Sjohnny 			 * this must be the ddivs no-intr case, and it returns
100970025d76Sjohnny 			 * DDI_SUCCESS instead of DDI_FAILURE.
101070025d76Sjohnny 			 */
101170025d76Sjohnny 			if (ddi_prop_get_int(DDI_DEV_T_ANY, child,
101270025d76Sjohnny 			    DDI_PROP_DONTPASS, "interrupts", -1) == -1)
101370025d76Sjohnny 				return (DDI_SUCCESS);
101470025d76Sjohnny 			/*
101570025d76Sjohnny 			 * Create the ddi_parent_private_data for a pseudo
101670025d76Sjohnny 			 * child.
101770025d76Sjohnny 			 */
101870025d76Sjohnny 			pci_common_set_parent_private_data(child);
101970025d76Sjohnny 			return (DDI_SUCCESS);
102070025d76Sjohnny 		}
102170025d76Sjohnny 
102270025d76Sjohnny 		/*
102370025d76Sjohnny 		 * The child was not merged into a h/w node,
102470025d76Sjohnny 		 * but there's not much we can do with it other
102570025d76Sjohnny 		 * than return failure to cause the node to be removed.
102670025d76Sjohnny 		 */
102770025d76Sjohnny 		cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
102870025d76Sjohnny 		    ddi_get_name(child), ddi_get_name_addr(child),
102970025d76Sjohnny 		    ddi_get_name(child));
103070025d76Sjohnny 		ddi_set_name_addr(child, NULL);
103170025d76Sjohnny 		return (DDI_NOT_WELL_FORMED);
103270025d76Sjohnny 	}
103370025d76Sjohnny 
103470025d76Sjohnny 	if (ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
103570025d76Sjohnny 	    "interrupts", -1) != -1)
103670025d76Sjohnny 		pci_common_set_parent_private_data(child);
103770025d76Sjohnny 	else
103870025d76Sjohnny 		ddi_set_parent_data(child, NULL);
103970025d76Sjohnny 
1040eae2e508Skrishnae 	/* Disable certain errors on PCIe drivers for x86 platforms */
1041eae2e508Skrishnae 	regs = pcie_get_aer_uce_mask() | npe_aer_uce_mask;
1042eae2e508Skrishnae 	pcie_set_aer_uce_mask(regs);
1043eae2e508Skrishnae 	regs = pcie_get_aer_ce_mask() | npe_aer_ce_mask;
1044eae2e508Skrishnae 	pcie_set_aer_ce_mask(regs);
1045eae2e508Skrishnae 	regs = pcie_get_aer_suce_mask() | npe_aer_suce_mask;
1046eae2e508Skrishnae 	pcie_set_aer_suce_mask(regs);
1047eae2e508Skrishnae 
104870025d76Sjohnny 	/*
1049eae2e508Skrishnae 	 * If URs are disabled, mask SERRs as well, otherwise the system will
1050eae2e508Skrishnae 	 * still be notified of URs
105170025d76Sjohnny 	 */
1052eae2e508Skrishnae 	if (npe_aer_uce_mask & PCIE_AER_UCE_UR)
1053eae2e508Skrishnae 		pcie_set_serr_mask(1);
1054eae2e508Skrishnae 
1055337fc9e2Sanish 	if (pci_config_setup(child, &cfg_hdl) == DDI_SUCCESS) {
1056337fc9e2Sanish 		npe_ck804_fix_aer_ptr(cfg_hdl);
1057a2de976fSPavel Potoplyak 		npe_nvidia_error_workaround(cfg_hdl);
1058a2de976fSPavel Potoplyak 		npe_intel_error_workaround(cfg_hdl);
1059337fc9e2Sanish 		pci_config_teardown(&cfg_hdl);
1060337fc9e2Sanish 	}
106170025d76Sjohnny 
1062c0da6274SZhi-Jun Robin Fu 	bus_p = PCIE_DIP2BUS(child);
1063eae2e508Skrishnae 	if (bus_p) {
1064eae2e508Skrishnae 		uint16_t device_id = (uint16_t)(bus_p->bus_dev_ven_id >> 16);
1065eae2e508Skrishnae 		uint16_t vendor_id = (uint16_t)(bus_p->bus_dev_ven_id & 0xFFFF);
1066eae2e508Skrishnae 		uint16_t rev_id = bus_p->bus_rev_id;
1067eae2e508Skrishnae 
1068eae2e508Skrishnae 		/* Disable AER for certain NVIDIA Chipsets */
1069eae2e508Skrishnae 		if ((vendor_id == NVIDIA_VENDOR_ID) &&
1070eae2e508Skrishnae 		    (device_id == NVIDIA_CK804_DEVICE_ID) &&
1071eae2e508Skrishnae 		    (rev_id < NVIDIA_CK804_AER_VALID_REVID))
1072eae2e508Skrishnae 			bus_p->bus_aer_off = 0;
1073eae2e508Skrishnae 
1074fc256490SJason Beloro 		pcie_init_dom(child);
1075eae2e508Skrishnae 		(void) pcie_initchild(child);
1076eae2e508Skrishnae 	}
1077eae2e508Skrishnae 
107870025d76Sjohnny 	return (DDI_SUCCESS);
107970025d76Sjohnny }
108070025d76Sjohnny 
108170025d76Sjohnny 
108270025d76Sjohnny static int
npe_removechild(dev_info_t * dip)108370025d76Sjohnny npe_removechild(dev_info_t *dip)
108470025d76Sjohnny {
1085eae2e508Skrishnae 	pcie_uninitchild(dip);
108670025d76Sjohnny 
108770025d76Sjohnny 	ddi_set_name_addr(dip, NULL);
108870025d76Sjohnny 
108970025d76Sjohnny 	/*
109070025d76Sjohnny 	 * Strip the node to properly convert it back to prototype form
109170025d76Sjohnny 	 */
109270025d76Sjohnny 	ddi_remove_minor_node(dip, NULL);
109370025d76Sjohnny 
109470025d76Sjohnny 	ddi_prop_remove_all(dip);
109570025d76Sjohnny 
109670025d76Sjohnny 	return (DDI_SUCCESS);
109770025d76Sjohnny }
109870025d76Sjohnny 
109970025d76Sjohnny static int
npe_open(dev_t * devp,int flags,int otyp,cred_t * credp)110070025d76Sjohnny npe_open(dev_t *devp, int flags, int otyp, cred_t *credp)
110170025d76Sjohnny {
110226947304SEvan Yan 	minor_t		minor = getminor(*devp);
110326947304SEvan Yan 	int		instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
110426947304SEvan Yan 	pci_state_t	*pci_p = ddi_get_soft_state(npe_statep, instance);
110526947304SEvan Yan 	int	rv;
110670025d76Sjohnny 
110726947304SEvan Yan 	/*
110826947304SEvan Yan 	 * Make sure the open is for the right file type.
110926947304SEvan Yan 	 */
111026947304SEvan Yan 	if (otyp != OTYP_CHR)
111126947304SEvan Yan 		return (EINVAL);
111226947304SEvan Yan 
111326947304SEvan Yan 	if (pci_p == NULL)
111426947304SEvan Yan 		return (ENXIO);
111526947304SEvan Yan 
111626947304SEvan Yan 	mutex_enter(&pci_p->pci_mutex);
111726947304SEvan Yan 	switch (PCI_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
111826947304SEvan Yan 	case PCI_TOOL_REG_MINOR_NUM:
111926947304SEvan Yan 	case PCI_TOOL_INTR_MINOR_NUM:
112026947304SEvan Yan 		break;
112126947304SEvan Yan 	default:
112226947304SEvan Yan 		/* Handle devctl ioctls */
112326947304SEvan Yan 		rv = pcie_open(pci_p->pci_dip, devp, flags, otyp, credp);
112426947304SEvan Yan 		mutex_exit(&pci_p->pci_mutex);
112526947304SEvan Yan 		return (rv);
112626947304SEvan Yan 	}
112726947304SEvan Yan 
112826947304SEvan Yan 	/* Handle pcitool ioctls */
112926947304SEvan Yan 	if (flags & FEXCL) {
113026947304SEvan Yan 		if (pci_p->pci_soft_state != PCI_SOFT_STATE_CLOSED) {
113126947304SEvan Yan 			mutex_exit(&pci_p->pci_mutex);
113226947304SEvan Yan 			cmn_err(CE_NOTE, "npe_open: busy");
113326947304SEvan Yan 			return (EBUSY);
113426947304SEvan Yan 		}
113526947304SEvan Yan 		pci_p->pci_soft_state = PCI_SOFT_STATE_OPEN_EXCL;
113626947304SEvan Yan 	} else {
113726947304SEvan Yan 		if (pci_p->pci_soft_state == PCI_SOFT_STATE_OPEN_EXCL) {
113826947304SEvan Yan 			mutex_exit(&pci_p->pci_mutex);
113926947304SEvan Yan 			cmn_err(CE_NOTE, "npe_open: busy");
114026947304SEvan Yan 			return (EBUSY);
114126947304SEvan Yan 		}
114226947304SEvan Yan 		pci_p->pci_soft_state = PCI_SOFT_STATE_OPEN;
114326947304SEvan Yan 	}
114426947304SEvan Yan 	mutex_exit(&pci_p->pci_mutex);
114526947304SEvan Yan 
114626947304SEvan Yan 	return (0);
114770025d76Sjohnny }
114870025d76Sjohnny 
114970025d76Sjohnny static int
npe_close(dev_t dev,int flags,int otyp,cred_t * credp)115026947304SEvan Yan npe_close(dev_t dev, int flags, int otyp, cred_t *credp)
115170025d76Sjohnny {
115270025d76Sjohnny 	minor_t		minor = getminor(dev);
115326947304SEvan Yan 	int		instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
115470025d76Sjohnny 	pci_state_t	*pci_p = ddi_get_soft_state(npe_statep, instance);
115526947304SEvan Yan 	int	rv;
115670025d76Sjohnny 
115770025d76Sjohnny 	if (pci_p == NULL)
115870025d76Sjohnny 		return (ENXIO);
115970025d76Sjohnny 
116026947304SEvan Yan 	mutex_enter(&pci_p->pci_mutex);
1161eae2e508Skrishnae 
116226947304SEvan Yan 	switch (PCI_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
116326947304SEvan Yan 	case PCI_TOOL_REG_MINOR_NUM:
116426947304SEvan Yan 	case PCI_TOOL_INTR_MINOR_NUM:
116526947304SEvan Yan 		break;
116626947304SEvan Yan 	default:
116726947304SEvan Yan 		/* Handle devctl ioctls */
116826947304SEvan Yan 		rv = pcie_close(pci_p->pci_dip, dev, flags, otyp, credp);
116926947304SEvan Yan 		mutex_exit(&pci_p->pci_mutex);
117026947304SEvan Yan 		return (rv);
117126947304SEvan Yan 	}
117270025d76Sjohnny 
117326947304SEvan Yan 	/* Handle pcitool ioctls */
117426947304SEvan Yan 	pci_p->pci_soft_state = PCI_SOFT_STATE_CLOSED;
117526947304SEvan Yan 	mutex_exit(&pci_p->pci_mutex);
117626947304SEvan Yan 	return (0);
117770025d76Sjohnny }
117870025d76Sjohnny 
117970025d76Sjohnny static int
npe_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)118026947304SEvan Yan npe_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
118170025d76Sjohnny {
118226947304SEvan Yan 	minor_t		minor = getminor(dev);
118326947304SEvan Yan 	int		instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
118426947304SEvan Yan 	pci_state_t	*pci_p = ddi_get_soft_state(npe_statep, instance);
118526947304SEvan Yan 	int		ret = ENOTTY;
118626947304SEvan Yan 
118726947304SEvan Yan 	if (pci_p == NULL)
118826947304SEvan Yan 		return (ENXIO);
118926947304SEvan Yan 
119026947304SEvan Yan 	switch (PCI_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
119126947304SEvan Yan 	case PCI_TOOL_REG_MINOR_NUM:
119226947304SEvan Yan 	case PCI_TOOL_INTR_MINOR_NUM:
119326947304SEvan Yan 		/* To handle pcitool related ioctls */
119426947304SEvan Yan 		ret =  pci_common_ioctl(pci_p->pci_dip, dev, cmd, arg, mode,
119526947304SEvan Yan 		    credp, rvalp);
119626947304SEvan Yan 		break;
119726947304SEvan Yan 	default:
119826947304SEvan Yan 		/* To handle devctl and hotplug related ioctls */
119926947304SEvan Yan 		ret = pcie_ioctl(pci_p->pci_dip, dev, cmd, arg, mode, credp,
120026947304SEvan Yan 		    rvalp);
120126947304SEvan Yan 		break;
120226947304SEvan Yan 	}
120326947304SEvan Yan 
120426947304SEvan Yan 	return (ret);
120570025d76Sjohnny }
1206247980beSanish 
120700d0963fSdilpreet /*ARGSUSED*/
1208247980beSanish static int
npe_fm_init(dev_info_t * dip,dev_info_t * tdip,int cap,ddi_iblock_cookie_t * ibc)120900d0963fSdilpreet npe_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap,
121000d0963fSdilpreet     ddi_iblock_cookie_t *ibc)
1211247980beSanish {
121200d0963fSdilpreet 	pci_state_t  *pcip = ddi_get_soft_state(npe_statep,
121300d0963fSdilpreet 	    ddi_get_instance(dip));
121400d0963fSdilpreet 
121500d0963fSdilpreet 	ASSERT(ibc != NULL);
121600d0963fSdilpreet 	*ibc = pcip->pci_fm_ibc;
121700d0963fSdilpreet 
121800d0963fSdilpreet 	return (pcip->pci_fmcap);
121900d0963fSdilpreet }
1220247980beSanish 
1221ffb64830SJordan Paige Hendricks static int
npe_bus_get_eventcookie(dev_info_t * dip,dev_info_t * rdip,char * eventname,ddi_eventcookie_t * cookiep)1222ffb64830SJordan Paige Hendricks npe_bus_get_eventcookie(dev_info_t *dip, dev_info_t *rdip, char *eventname,
1223ffb64830SJordan Paige Hendricks     ddi_eventcookie_t *cookiep)
1224ffb64830SJordan Paige Hendricks {
1225ffb64830SJordan Paige Hendricks 	pci_state_t *pcip = ddi_get_soft_state(npe_statep,
1226ffb64830SJordan Paige Hendricks 	    ddi_get_instance(dip));
1227ffb64830SJordan Paige Hendricks 
1228ffb64830SJordan Paige Hendricks 	return (ndi_event_retrieve_cookie(pcip->pci_ndi_event_hdl, rdip,
1229ffb64830SJordan Paige Hendricks 	    eventname, cookiep, NDI_EVENT_NOPASS));
1230ffb64830SJordan Paige Hendricks }
1231ffb64830SJordan Paige Hendricks 
1232ffb64830SJordan Paige Hendricks static int
npe_bus_add_eventcall(dev_info_t * dip,dev_info_t * rdip,ddi_eventcookie_t cookie,void (* callback)(dev_info_t * dip,ddi_eventcookie_t cookie,void * arg,void * bus_impldata),void * arg,ddi_callback_id_t * cb_id)1233ffb64830SJordan Paige Hendricks npe_bus_add_eventcall(dev_info_t *dip, dev_info_t *rdip,
1234ffb64830SJordan Paige Hendricks     ddi_eventcookie_t cookie, void (*callback)(dev_info_t *dip,
1235ffb64830SJordan Paige Hendricks     ddi_eventcookie_t cookie, void *arg, void *bus_impldata),
1236ffb64830SJordan Paige Hendricks     void *arg, ddi_callback_id_t *cb_id)
1237ffb64830SJordan Paige Hendricks {
1238ffb64830SJordan Paige Hendricks 	pci_state_t *pcip = ddi_get_soft_state(npe_statep,
1239ffb64830SJordan Paige Hendricks 	    ddi_get_instance(dip));
1240ffb64830SJordan Paige Hendricks 
1241ffb64830SJordan Paige Hendricks 	return (ndi_event_add_callback(pcip->pci_ndi_event_hdl, rdip, cookie,
1242ffb64830SJordan Paige Hendricks 	    callback, arg, NDI_SLEEP, cb_id));
1243ffb64830SJordan Paige Hendricks }
1244ffb64830SJordan Paige Hendricks 
1245ffb64830SJordan Paige Hendricks static int
npe_bus_remove_eventcall(dev_info_t * dip,ddi_callback_id_t cb_id)1246ffb64830SJordan Paige Hendricks npe_bus_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
1247ffb64830SJordan Paige Hendricks {
1248ffb64830SJordan Paige Hendricks 	pci_state_t *pcip = ddi_get_soft_state(npe_statep,
1249ffb64830SJordan Paige Hendricks 	    ddi_get_instance(dip));
1250ffb64830SJordan Paige Hendricks 	return (ndi_event_remove_callback(pcip->pci_ndi_event_hdl, cb_id));
1251ffb64830SJordan Paige Hendricks }
1252ffb64830SJordan Paige Hendricks 
1253ffb64830SJordan Paige Hendricks static int
npe_bus_post_event(dev_info_t * dip,dev_info_t * rdip,ddi_eventcookie_t cookie,void * impl_data)1254ffb64830SJordan Paige Hendricks npe_bus_post_event(dev_info_t *dip, dev_info_t *rdip,
1255ffb64830SJordan Paige Hendricks     ddi_eventcookie_t cookie, void *impl_data)
1256ffb64830SJordan Paige Hendricks {
1257ffb64830SJordan Paige Hendricks 	pci_state_t *pcip = ddi_get_soft_state(npe_statep,
1258ffb64830SJordan Paige Hendricks 	    ddi_get_instance(dip));
1259ffb64830SJordan Paige Hendricks 	return (ndi_event_do_callback(pcip->pci_ndi_event_hdl, rdip, cookie,
1260ffb64830SJordan Paige Hendricks 	    impl_data));
1261ffb64830SJordan Paige Hendricks 
1262ffb64830SJordan Paige Hendricks }
1263ffb64830SJordan Paige Hendricks 
126400d0963fSdilpreet /*ARGSUSED*/
126500d0963fSdilpreet static int
npe_fm_callback(dev_info_t * dip,ddi_fm_error_t * derr,const void * no_used)126600d0963fSdilpreet npe_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *no_used)
126700d0963fSdilpreet {
1268eae2e508Skrishnae 	/*
1269eae2e508Skrishnae 	 * On current x86 systems, npe's callback does not get called for failed
1270eae2e508Skrishnae 	 * loads.  If in the future this feature is used, the fault PA should be
1271eae2e508Skrishnae 	 * logged in the derr->fme_bus_specific field.  The appropriate PCIe
1272eae2e508Skrishnae 	 * error handling code should be called and needs to be coordinated with
1273eae2e508Skrishnae 	 * safe access handling.
1274eae2e508Skrishnae 	 */
1275eae2e508Skrishnae 
1276eae2e508Skrishnae 	return (DDI_FM_OK);
1277247980beSanish }
1278