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 *)®
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 *)®
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 ®len) != 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