xref: /illumos-gate/usr/src/uts/common/io/pciex/pcieb.c (revision ac91f7fc)
1d4bc0535SKrishna Elango /*
2d4bc0535SKrishna Elango  * CDDL HEADER START
3d4bc0535SKrishna Elango  *
4d4bc0535SKrishna Elango  * The contents of this file are subject to the terms of the
5d4bc0535SKrishna Elango  * Common Development and Distribution License (the "License").
6d4bc0535SKrishna Elango  * You may not use this file except in compliance with the License.
7d4bc0535SKrishna Elango  *
8d4bc0535SKrishna Elango  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d4bc0535SKrishna Elango  * or http://www.opensolaris.org/os/licensing.
10d4bc0535SKrishna Elango  * See the License for the specific language governing permissions
11d4bc0535SKrishna Elango  * and limitations under the License.
12d4bc0535SKrishna Elango  *
13d4bc0535SKrishna Elango  * When distributing Covered Code, include this CDDL HEADER in each
14d4bc0535SKrishna Elango  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d4bc0535SKrishna Elango  * If applicable, add the following below this CDDL HEADER, with the
16d4bc0535SKrishna Elango  * fields enclosed by brackets "[]" replaced with your own identifying
17d4bc0535SKrishna Elango  * information: Portions Copyright [yyyy] [name of copyright owner]
18d4bc0535SKrishna Elango  *
19d4bc0535SKrishna Elango  * CDDL HEADER END
20d4bc0535SKrishna Elango  */
21d4bc0535SKrishna Elango /*
222e98bdabSvitezslav batrla - Sun Microsystems - Prague Czech Republic  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23d4bc0535SKrishna Elango  */
24cd21e7c5SGarrett D'Amore /*
25cd21e7c5SGarrett D'Amore  * Copyright 2012 Garrett D'Amore <garrett@damore.org>.  All rights reserved.
26b3d69c05SRobert Mustacchi  * Copyright 2019 Joyent, Inc.
27*ac91f7fcSKeith M Wesolowski  * Copyright 2023 Oxide Computer Company
28cd21e7c5SGarrett D'Amore  */
29d4bc0535SKrishna Elango 
30d4bc0535SKrishna Elango /*
31d4bc0535SKrishna Elango  * Common x86 and SPARC PCI-E to PCI bus bridge nexus driver
32b3d69c05SRobert Mustacchi  *
33b3d69c05SRobert Mustacchi  * Background
34b3d69c05SRobert Mustacchi  * ----------
35b3d69c05SRobert Mustacchi  *
36b3d69c05SRobert Mustacchi  * The PCI Express (PCIe) specification defines that all of the PCIe devices in
37b3d69c05SRobert Mustacchi  * the system are connected together in a series of different fabrics. A way to
38b3d69c05SRobert Mustacchi  * think of these fabrics is that they are small networks where there are links
39b3d69c05SRobert Mustacchi  * between different devices and switches that allow fan out or fan in of the
40b3d69c05SRobert Mustacchi  * fabric. The entry point to that fabric is called a root complex and the
41b3d69c05SRobert Mustacchi  * fabric terminates at a what is called an endpoint, which is really just PCIe
42b3d69c05SRobert Mustacchi  * terminology for the common cards that are inserted into the system (HBAs,
43b3d69c05SRobert Mustacchi  * NICs, USB, NVMe, etc.).
44b3d69c05SRobert Mustacchi  *
45b3d69c05SRobert Mustacchi  * The PCIe specification states that every link on the system has a virtual
46b3d69c05SRobert Mustacchi  * PCI-to-PCI bridge. This allows PCIe devices to still be configured the same
47b3d69c05SRobert Mustacchi  * way traditional PCI devices are to the operating system and allows them to
48b3d69c05SRobert Mustacchi  * have a traditional PCI bus, device, and function associated with them, even
49b3d69c05SRobert Mustacchi  * though there is no actual shared bus. In addition, bridges are also used to
50b3d69c05SRobert Mustacchi  * connect traditional PCI and PCI-X devices into them.
51b3d69c05SRobert Mustacchi  *
52b3d69c05SRobert Mustacchi  * The PCIe specification refers to upstream and downstream ports. Upstream
53b3d69c05SRobert Mustacchi  * ports are considered closer the root complex and downstream ports are closer
54b3d69c05SRobert Mustacchi  * to the endpoint. We can divide the devices that the bridge driver attaches to
55b3d69c05SRobert Mustacchi  * into two groups. Those that are considered upstream ports, these include root
56b3d69c05SRobert Mustacchi  * complexes and parts of PCIe switches. And downstream ports, which are the
57b3d69c05SRobert Mustacchi  * other half of PCIe switches and endpoints (which this driver does not attach
58b3d69c05SRobert Mustacchi  * to, normal hardware-specific or class-specific drivers attach to those).
59b3d69c05SRobert Mustacchi  *
60b3d69c05SRobert Mustacchi  * Interrupt Management
61b3d69c05SRobert Mustacchi  * --------------------
62b3d69c05SRobert Mustacchi  *
63b3d69c05SRobert Mustacchi  * Upstream ports of bridges have additional things that we care about.
64b3d69c05SRobert Mustacchi  * Specifically they're the means through which we find out about:
65b3d69c05SRobert Mustacchi  *
66b3d69c05SRobert Mustacchi  *  - Advanced Error Reporting (AERs)
67b3d69c05SRobert Mustacchi  *  - Hotplug events
68b3d69c05SRobert Mustacchi  *  - Link Bandwidth Events
69b3d69c05SRobert Mustacchi  *  - Power Management Events (PME)
70b3d69c05SRobert Mustacchi  *
71b3d69c05SRobert Mustacchi  * Each of these features is an optional feature (though ones we hope are
72b3d69c05SRobert Mustacchi  * implemented). The features above are grouped into two different buckets based
73b3d69c05SRobert Mustacchi  * on which PCI capability they appear in. AER management is done through a PCI
74b3d69c05SRobert Mustacchi  * Express extended configuration header (it lives in extended PCI configuration
75b3d69c05SRobert Mustacchi  * space) called the 'Advanced Error Reporting Extended Capability'. The other
76b3d69c05SRobert Mustacchi  * events are all managed as part of the 'PCI Express Capability Structure'.
77b3d69c05SRobert Mustacchi  * This structure is found in traditional PCI configuration space.
78b3d69c05SRobert Mustacchi  *
79b3d69c05SRobert Mustacchi  * The way that the interrupts are programmed for these types of events differs
80b3d69c05SRobert Mustacchi  * a bit from the way one might expect a normal device to operate. For most
81b3d69c05SRobert Mustacchi  * devices, one allocates a number of interrupts based on a combination of what
82b3d69c05SRobert Mustacchi  * the device supports, what the OS supports per device, and the number the
83b3d69c05SRobert Mustacchi  * driver needs. Then the driver programs the device in a device-specific manner
84b3d69c05SRobert Mustacchi  * to indicate which events should trigger a specific interrupt vector.
85b3d69c05SRobert Mustacchi  *
86b3d69c05SRobert Mustacchi  * However, for both the AER and PCI capabilities, the driver has to do
87b3d69c05SRobert Mustacchi  * something different. The driver first allocates interrupts by programming the
88b3d69c05SRobert Mustacchi  * MSI or MSI-X table and then asks the device which interrupts have been
89b3d69c05SRobert Mustacchi  * assigned to these purposes. Because these events are only supported in
90b3d69c05SRobert Mustacchi  * 'upstream' devices, this does not interfere with the traditional management
91b3d69c05SRobert Mustacchi  * of MSI and MSI-X interrupts. At this time, the pcieb driver only supports the
92b3d69c05SRobert Mustacchi  * use of MSI interrupts.
93b3d69c05SRobert Mustacchi  *
94b3d69c05SRobert Mustacchi  * Once the interrupts have been allocated, we read back which vectors have been
95b3d69c05SRobert Mustacchi  * nominated by the device to cover the corresponding capability. The interrupt
96b3d69c05SRobert Mustacchi  * is allocated on a per-capability basis. Therefore, one interrupt would cover
97b3d69c05SRobert Mustacchi  * AERs, while another interrupt would cover the rest of the desired functions.
98*ac91f7fcSKeith M Wesolowski  * Importantly, there is no guarantee that a bridge supports more than one
99*ac91f7fcSKeith M Wesolowski  * vector; in particular, at least some AMD bridges do not.  In this case,
100*ac91f7fcSKeith M Wesolowski  * interrupts associated with all the available capabilities will be routed to
101*ac91f7fcSKeith M Wesolowski  * the same shared vector.
102b3d69c05SRobert Mustacchi  *
103b3d69c05SRobert Mustacchi  * To track which interrupts cover which behaviors, each driver state
104b3d69c05SRobert Mustacchi  * (pcieb_devstate_t) has a member called 'pcieb_isr_tab'. Each index represents
105b3d69c05SRobert Mustacchi  * an interrupt vector and there are a series of flags that represent the
106b3d69c05SRobert Mustacchi  * different possible interrupt sources: PCIEB_INTR_SRC_HP (hotplug),
107b3d69c05SRobert Mustacchi  * PCEIB_INTR_SRC_PME (power management event), PCIEB_INTR_SRC_AER (error
108b3d69c05SRobert Mustacchi  * reporting), PCIEB_INTR_SRC_LBW (link bandwidth).
109b3d69c05SRobert Mustacchi  *
110b3d69c05SRobert Mustacchi  * Because the hotplug, link bandwidth, and power management events all share
111b3d69c05SRobert Mustacchi  * the same vector, if an interrupt comes in, we must check all of the enabled
112b3d69c05SRobert Mustacchi  * sources that might generate this interrupt. It is highly likely that more
113b3d69c05SRobert Mustacchi  * than one will fire at the same time, for example, a hotplug event that fires
114b3d69c05SRobert Mustacchi  * because a device has been inserted or removed, will likely trigger a link
115b3d69c05SRobert Mustacchi  * bandwidth event.
116b3d69c05SRobert Mustacchi  *
117b3d69c05SRobert Mustacchi  * The pcieb driver itself does not actually have much logic to deal with and
118b3d69c05SRobert Mustacchi  * clear the interrupts in question. It generally speaking will vector most
119b3d69c05SRobert Mustacchi  * events back to the more general pcie driver or, in the case of AERs, initiate
120b3d69c05SRobert Mustacchi  * a scan of the fabric itself (also part of the pcie driver).
121b3d69c05SRobert Mustacchi  *
122b3d69c05SRobert Mustacchi  * Link Management
123b3d69c05SRobert Mustacchi  * ---------------
124b3d69c05SRobert Mustacchi  *
125b3d69c05SRobert Mustacchi  * The pcieb driver is used to take care of two different aspects of link
126b3d69c05SRobert Mustacchi  * management. The first of these, as described briefly above, is to monitor for
127b3d69c05SRobert Mustacchi  * changes to the negotiated link bandwidth. These events are managed by
128b3d69c05SRobert Mustacchi  * enabling support for the interrupts in the PCI Express Capability Structure.
129b3d69c05SRobert Mustacchi  * This is all taken care of by the pcie driver through functions like
130f7afc1fdSRobert Mustacchi  * pcie_link_bw_enable().
131b3d69c05SRobert Mustacchi  *
132b3d69c05SRobert Mustacchi  * The second aspect of link management the pcieb driver enables is the ability
133b3d69c05SRobert Mustacchi  * to retrain the link and optionally limit the speed. This is enabled through a
134b3d69c05SRobert Mustacchi  * series of private ioctls that are driven through a private userland utility,
135b3d69c05SRobert Mustacchi  * /usr/lib/pci/pcieb. Eventually, this should be more fleshed out and a more
136b3d69c05SRobert Mustacchi  * uniform interface based around the devctls that can be leveraged across
137b3d69c05SRobert Mustacchi  * different classes of devices should be used.
138b3d69c05SRobert Mustacchi  *
139b3d69c05SRobert Mustacchi  * Under the hood this basically leverages the ability of the upstream port to
140b3d69c05SRobert Mustacchi  * retrain a link by writing a bit to the PCIe link control register. See
141b3d69c05SRobert Mustacchi  * pcieb_ioctl_retrain(). From there, if the driver ever receives a request to
142b3d69c05SRobert Mustacchi  * change the maximum speed, that is updated in the card; however, it does not
143b3d69c05SRobert Mustacchi  * immediately retrain the link. A separate ioctl request is required to do so.
144b3d69c05SRobert Mustacchi  * Once the speed has been changed, regardless of whether or not it has been
145b3d69c05SRobert Mustacchi  * retrained, that fact will always be noted.
146d4bc0535SKrishna Elango  */
147d4bc0535SKrishna Elango 
148d4bc0535SKrishna Elango #include <sys/sysmacros.h>
149d4bc0535SKrishna Elango #include <sys/conf.h>
150d4bc0535SKrishna Elango #include <sys/kmem.h>
151d4bc0535SKrishna Elango #include <sys/debug.h>
152d4bc0535SKrishna Elango #include <sys/modctl.h>
153d4bc0535SKrishna Elango #include <sys/autoconf.h>
154d4bc0535SKrishna Elango #include <sys/ddi_impldefs.h>
155d4bc0535SKrishna Elango #include <sys/pci.h>
156d4bc0535SKrishna Elango #include <sys/ddi.h>
157d4bc0535SKrishna Elango #include <sys/sunddi.h>
158d4bc0535SKrishna Elango #include <sys/sunndi.h>
159d4bc0535SKrishna Elango #include <sys/fm/util.h>
160d4bc0535SKrishna Elango #include <sys/pci_cap.h>
16126947304SEvan Yan #include <sys/pci_impl.h>
162d4bc0535SKrishna Elango #include <sys/pcie_impl.h>
163d4bc0535SKrishna Elango #include <sys/open.h>
164d4bc0535SKrishna Elango #include <sys/stat.h>
165d4bc0535SKrishna Elango #include <sys/file.h>
166d4bc0535SKrishna Elango #include <sys/promif.h>		/* prom_printf */
167d4bc0535SKrishna Elango #include <sys/disp.h>
168d4bc0535SKrishna Elango #include <sys/pcie_pwr.h>
16926947304SEvan Yan #include <sys/hotplug/pci/pcie_hp.h>
170d4bc0535SKrishna Elango #include "pcieb.h"
171b3d69c05SRobert Mustacchi #include "pcieb_ioctl.h"
172d4bc0535SKrishna Elango #ifdef PX_PLX
173d4bc0535SKrishna Elango #include <io/pciex/pcieb_plx.h>
174d4bc0535SKrishna Elango #endif /* PX_PLX */
175d4bc0535SKrishna Elango 
176d4bc0535SKrishna Elango /*LINTLIBRARY*/
177d4bc0535SKrishna Elango 
178d4bc0535SKrishna Elango /* panic flag */
179d4bc0535SKrishna Elango int pcieb_die = PF_ERR_FATAL_FLAGS;
18083e6495bSDaniel Ice int pcieb_disable_41210_wkarnd = 0;
181d4bc0535SKrishna Elango 
182d4bc0535SKrishna Elango /* flag to turn on MSI support */
18386a9c507SGuoli Shu int pcieb_enable_msi = 1;
184d4bc0535SKrishna Elango 
185d4bc0535SKrishna Elango #if defined(DEBUG)
186d4bc0535SKrishna Elango uint_t pcieb_dbg_print = 0;
187d4bc0535SKrishna Elango 
188d4bc0535SKrishna Elango static char *pcieb_debug_sym [] = {	/* same sequence as pcieb_debug_bit */
189d4bc0535SKrishna Elango 	/*  0 */ "attach",
190d4bc0535SKrishna Elango 	/*  1 */ "pwr",
191d4bc0535SKrishna Elango 	/*  2 */ "intr"
192d4bc0535SKrishna Elango };
193d4bc0535SKrishna Elango #endif /* DEBUG */
194d4bc0535SKrishna Elango 
195d4bc0535SKrishna Elango static int pcieb_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *, off_t,
196d4bc0535SKrishna Elango 	off_t, caddr_t *);
197d4bc0535SKrishna Elango static int pcieb_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
198d4bc0535SKrishna Elango 	void *);
199d4bc0535SKrishna Elango static int pcieb_fm_init(pcieb_devstate_t *pcieb_p);
200d4bc0535SKrishna Elango static void pcieb_fm_fini(pcieb_devstate_t *pcieb_p);
201d4bc0535SKrishna Elango static int pcieb_fm_init_child(dev_info_t *dip, dev_info_t *cdip, int cap,
202d4bc0535SKrishna Elango     ddi_iblock_cookie_t *ibc_p);
203d4bc0535SKrishna Elango static int pcieb_dma_allochdl(dev_info_t *dip, dev_info_t *rdip,
204d4bc0535SKrishna Elango 	ddi_dma_attr_t *attr_p, int (*waitfp)(caddr_t), caddr_t arg,
205d4bc0535SKrishna Elango 	ddi_dma_handle_t *handlep);
206d4bc0535SKrishna Elango static int pcieb_dma_mctl(dev_info_t *dip, dev_info_t *rdip,
207d4bc0535SKrishna Elango 	ddi_dma_handle_t handle, enum ddi_dma_ctlops cmd, off_t *offp,
208d4bc0535SKrishna Elango 	size_t *lenp, caddr_t *objp, uint_t cache_flags);
209d4bc0535SKrishna Elango static int pcieb_intr_ops(dev_info_t *dip, dev_info_t *rdip,
210d4bc0535SKrishna Elango 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
211d4bc0535SKrishna Elango 
212d4bc0535SKrishna Elango static struct bus_ops pcieb_bus_ops = {
213d4bc0535SKrishna Elango 	BUSO_REV,
214d4bc0535SKrishna Elango 	pcieb_bus_map,
215d4bc0535SKrishna Elango 	0,
216d4bc0535SKrishna Elango 	0,
217d4bc0535SKrishna Elango 	0,
218d4bc0535SKrishna Elango 	i_ddi_map_fault,
219cd21e7c5SGarrett D'Amore 	0,
220d4bc0535SKrishna Elango 	pcieb_dma_allochdl,
221d4bc0535SKrishna Elango 	ddi_dma_freehdl,
222d4bc0535SKrishna Elango 	ddi_dma_bindhdl,
223d4bc0535SKrishna Elango 	ddi_dma_unbindhdl,
224d4bc0535SKrishna Elango 	ddi_dma_flush,
225d4bc0535SKrishna Elango 	ddi_dma_win,
226d4bc0535SKrishna Elango 	pcieb_dma_mctl,
227d4bc0535SKrishna Elango 	pcieb_ctlops,
228d4bc0535SKrishna Elango 	ddi_bus_prop_op,
229d4bc0535SKrishna Elango 	ndi_busop_get_eventcookie,	/* (*bus_get_eventcookie)();	*/
230d4bc0535SKrishna Elango 	ndi_busop_add_eventcall,	/* (*bus_add_eventcall)();	*/
231d4bc0535SKrishna Elango 	ndi_busop_remove_eventcall,	/* (*bus_remove_eventcall)();	*/
232d4bc0535SKrishna Elango 	ndi_post_event,			/* (*bus_post_event)();		*/
233d4bc0535SKrishna Elango 	NULL,				/* (*bus_intr_ctl)();		*/
23421922c75SToomas Soome 	NULL,				/* (*bus_config)();		*/
23521922c75SToomas Soome 	NULL,				/* (*bus_unconfig)();		*/
23621922c75SToomas Soome 	pcieb_fm_init_child,		/* (*bus_fm_init)();		*/
23721922c75SToomas Soome 	NULL,				/* (*bus_fm_fini)();		*/
23821922c75SToomas Soome 	i_ndi_busop_access_enter,	/* (*bus_fm_access_enter)();	*/
23921922c75SToomas Soome 	i_ndi_busop_access_exit,	/* (*bus_fm_access_exit)();	*/
24021922c75SToomas Soome 	pcie_bus_power,			/* (*bus_power)();		*/
24121922c75SToomas Soome 	pcieb_intr_ops,			/* (*bus_intr_op)();		*/
24221922c75SToomas Soome 	pcie_hp_common_ops		/* (*bus_hp_op)();		*/
243d4bc0535SKrishna Elango };
244d4bc0535SKrishna Elango 
245d4bc0535SKrishna Elango static int	pcieb_open(dev_t *, int, int, cred_t *);
246d4bc0535SKrishna Elango static int	pcieb_close(dev_t, int, int, cred_t *);
247d4bc0535SKrishna Elango static int	pcieb_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
248d4bc0535SKrishna Elango static int	pcieb_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
24921922c75SToomas Soome static uint_t	pcieb_intr_handler(caddr_t arg1, caddr_t arg2);
250d4bc0535SKrishna Elango 
251d4bc0535SKrishna Elango /* PM related functions */
252d4bc0535SKrishna Elango static int	pcieb_pwr_setup(dev_info_t *dip);
253d4bc0535SKrishna Elango static int	pcieb_pwr_init_and_raise(dev_info_t *dip, pcie_pwr_t *pwr_p);
254d4bc0535SKrishna Elango static void	pcieb_pwr_teardown(dev_info_t *dip);
255d4bc0535SKrishna Elango static int	pcieb_pwr_disable(dev_info_t *dip);
256d4bc0535SKrishna Elango 
257d4bc0535SKrishna Elango /* Hotplug related functions */
258d4bc0535SKrishna Elango static void pcieb_id_props(pcieb_devstate_t *pcieb);
259d4bc0535SKrishna Elango 
260d4bc0535SKrishna Elango /*
261d4bc0535SKrishna Elango  * soft state pointer
262d4bc0535SKrishna Elango  */
263d4bc0535SKrishna Elango void *pcieb_state;
264d4bc0535SKrishna Elango 
265d4bc0535SKrishna Elango static struct cb_ops pcieb_cb_ops = {
266d4bc0535SKrishna Elango 	pcieb_open,			/* open */
267d4bc0535SKrishna Elango 	pcieb_close,			/* close */
268d4bc0535SKrishna Elango 	nodev,				/* strategy */
269d4bc0535SKrishna Elango 	nodev,				/* print */
270d4bc0535SKrishna Elango 	nodev,				/* dump */
271d4bc0535SKrishna Elango 	nodev,				/* read */
272d4bc0535SKrishna Elango 	nodev,				/* write */
273d4bc0535SKrishna Elango 	pcieb_ioctl,			/* ioctl */
274d4bc0535SKrishna Elango 	nodev,				/* devmap */
275d4bc0535SKrishna Elango 	nodev,				/* mmap */
276d4bc0535SKrishna Elango 	nodev,				/* segmap */
277d4bc0535SKrishna Elango 	nochpoll,			/* poll */
27826947304SEvan Yan 	pcie_prop_op,			/* cb_prop_op */
279d4bc0535SKrishna Elango 	NULL,				/* streamtab */
280d4bc0535SKrishna Elango 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
281d4bc0535SKrishna Elango 	CB_REV,				/* rev */
282d4bc0535SKrishna Elango 	nodev,				/* int (*cb_aread)() */
283d4bc0535SKrishna Elango 	nodev				/* int (*cb_awrite)() */
284d4bc0535SKrishna Elango };
285d4bc0535SKrishna Elango 
286d4bc0535SKrishna Elango static int	pcieb_probe(dev_info_t *);
287d4bc0535SKrishna Elango static int	pcieb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
288d4bc0535SKrishna Elango static int	pcieb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
289d4bc0535SKrishna Elango 
290d4bc0535SKrishna Elango static struct dev_ops pcieb_ops = {
291d4bc0535SKrishna Elango 	DEVO_REV,		/* devo_rev */
292d4bc0535SKrishna Elango 	0,			/* refcnt  */
293d4bc0535SKrishna Elango 	pcieb_info,		/* info */
294d4bc0535SKrishna Elango 	nulldev,		/* identify */
295d4bc0535SKrishna Elango 	pcieb_probe,		/* probe */
296d4bc0535SKrishna Elango 	pcieb_attach,		/* attach */
297d4bc0535SKrishna Elango 	pcieb_detach,		/* detach */
298d4bc0535SKrishna Elango 	nulldev,		/* reset */
299d4bc0535SKrishna Elango 	&pcieb_cb_ops,		/* driver operations */
300d4bc0535SKrishna Elango 	&pcieb_bus_ops,		/* bus operations */
301d4bc0535SKrishna Elango 	pcie_power,		/* power */
302d4bc0535SKrishna Elango 	ddi_quiesce_not_needed,		/* quiesce */
303d4bc0535SKrishna Elango };
304d4bc0535SKrishna Elango 
305d4bc0535SKrishna Elango /*
306d4bc0535SKrishna Elango  * Module linkage information for the kernel.
307d4bc0535SKrishna Elango  */
308d4bc0535SKrishna Elango 
309d4bc0535SKrishna Elango static struct modldrv modldrv = {
310d4bc0535SKrishna Elango 	&mod_driverops, /* Type of module */
31126947304SEvan Yan 	"PCIe bridge/switch driver",
312d4bc0535SKrishna Elango 	&pcieb_ops,	/* driver ops */
313d4bc0535SKrishna Elango };
314d4bc0535SKrishna Elango 
315d4bc0535SKrishna Elango static struct modlinkage modlinkage = {
316d4bc0535SKrishna Elango 	MODREV_1,
317d4bc0535SKrishna Elango 	(void *)&modldrv,
318d4bc0535SKrishna Elango 	NULL
319d4bc0535SKrishna Elango };
320d4bc0535SKrishna Elango 
321d4bc0535SKrishna Elango /*
322d4bc0535SKrishna Elango  * forward function declarations:
323d4bc0535SKrishna Elango  */
324d4bc0535SKrishna Elango static void	pcieb_uninitchild(dev_info_t *);
32521922c75SToomas Soome static int	pcieb_initchild(dev_info_t *child);
326d4bc0535SKrishna Elango static void	pcieb_create_ranges_prop(dev_info_t *, ddi_acc_handle_t);
327d4bc0535SKrishna Elango static boolean_t pcieb_is_pcie_device_type(dev_info_t *dip);
328d4bc0535SKrishna Elango 
329d4bc0535SKrishna Elango /* interrupt related declarations */
330d4bc0535SKrishna Elango static int	pcieb_msi_supported(dev_info_t *);
331d4bc0535SKrishna Elango static int	pcieb_intr_attach(pcieb_devstate_t *pcieb);
332d4bc0535SKrishna Elango static int	pcieb_intr_init(pcieb_devstate_t *pcieb_p, int intr_type);
333d4bc0535SKrishna Elango static void	pcieb_intr_fini(pcieb_devstate_t *pcieb_p);
334d4bc0535SKrishna Elango 
335d4bc0535SKrishna Elango int
_init(void)336d4bc0535SKrishna Elango _init(void)
337d4bc0535SKrishna Elango {
338d4bc0535SKrishna Elango 	int e;
339d4bc0535SKrishna Elango 
340d4bc0535SKrishna Elango 	if ((e = ddi_soft_state_init(&pcieb_state, sizeof (pcieb_devstate_t),
341d4bc0535SKrishna Elango 	    1)) == 0 && (e = mod_install(&modlinkage)) != 0)
342d4bc0535SKrishna Elango 		ddi_soft_state_fini(&pcieb_state);
343d4bc0535SKrishna Elango 	return (e);
344d4bc0535SKrishna Elango }
345d4bc0535SKrishna Elango 
346d4bc0535SKrishna Elango int
_fini(void)347d4bc0535SKrishna Elango _fini(void)
348d4bc0535SKrishna Elango {
349d4bc0535SKrishna Elango 	int e;
350d4bc0535SKrishna Elango 
351d4bc0535SKrishna Elango 	if ((e = mod_remove(&modlinkage)) == 0) {
352d4bc0535SKrishna Elango 		ddi_soft_state_fini(&pcieb_state);
353d4bc0535SKrishna Elango 	}
354d4bc0535SKrishna Elango 	return (e);
355d4bc0535SKrishna Elango }
356d4bc0535SKrishna Elango 
357d4bc0535SKrishna Elango int
_info(struct modinfo * modinfop)358d4bc0535SKrishna Elango _info(struct modinfo *modinfop)
359d4bc0535SKrishna Elango {
360d4bc0535SKrishna Elango 	return (mod_info(&modlinkage, modinfop));
361d4bc0535SKrishna Elango }
362d4bc0535SKrishna Elango 
36326947304SEvan Yan /* ARGSUSED */
36426947304SEvan Yan static int
pcieb_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)36526947304SEvan Yan pcieb_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
36626947304SEvan Yan {
36726947304SEvan Yan 	minor_t		minor = getminor((dev_t)arg);
36826947304SEvan Yan 	int		instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
36926947304SEvan Yan 	pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, instance);
37026947304SEvan Yan 	int		ret = DDI_SUCCESS;
37126947304SEvan Yan 
37226947304SEvan Yan 	switch (infocmd) {
37326947304SEvan Yan 	case DDI_INFO_DEVT2INSTANCE:
37426947304SEvan Yan 		*result = (void *)(intptr_t)instance;
37526947304SEvan Yan 		break;
37626947304SEvan Yan 	case DDI_INFO_DEVT2DEVINFO:
37726947304SEvan Yan 		if (pcieb == NULL) {
37826947304SEvan Yan 			ret = DDI_FAILURE;
37926947304SEvan Yan 			break;
38026947304SEvan Yan 		}
38126947304SEvan Yan 
38226947304SEvan Yan 		*result = (void *)pcieb->pcieb_dip;
38326947304SEvan Yan 		break;
38426947304SEvan Yan 	default:
38526947304SEvan Yan 		ret = DDI_FAILURE;
38626947304SEvan Yan 		break;
38726947304SEvan Yan 	}
38826947304SEvan Yan 
38926947304SEvan Yan 	return (ret);
39026947304SEvan Yan }
39126947304SEvan Yan 
39226947304SEvan Yan 
393d4bc0535SKrishna Elango /*ARGSUSED*/
394d4bc0535SKrishna Elango static int
pcieb_probe(dev_info_t * devi)395d4bc0535SKrishna Elango pcieb_probe(dev_info_t *devi)
396d4bc0535SKrishna Elango {
397d4bc0535SKrishna Elango 	return (DDI_PROBE_SUCCESS);
398d4bc0535SKrishna Elango }
399d4bc0535SKrishna Elango 
40083e6495bSDaniel Ice /*
40183e6495bSDaniel Ice  * This is a workaround for an undocumented HW erratum with the
40283e6495bSDaniel Ice  * multi-function, F0 and F2, Intel 41210 PCIe-to-PCI bridge. When
40383e6495bSDaniel Ice  * Fn (cdip) attaches, this workaround is called to initialize Fn's
40483e6495bSDaniel Ice  * sibling (sdip) with MPS/MRRS if it isn't already configured.
40583e6495bSDaniel Ice  * Doing so prevents a malformed TLP panic.
40683e6495bSDaniel Ice  */
40783e6495bSDaniel Ice static void
pcieb_41210_mps_wkrnd(dev_info_t * cdip)40883e6495bSDaniel Ice pcieb_41210_mps_wkrnd(dev_info_t *cdip)
40983e6495bSDaniel Ice {
41083e6495bSDaniel Ice 	dev_info_t *sdip;
41183e6495bSDaniel Ice 	ddi_acc_handle_t cfg_hdl;
41283e6495bSDaniel Ice 	uint16_t cdip_dev_ctrl, cdip_mrrs_mps;
41383e6495bSDaniel Ice 	pcie_bus_t *cdip_bus_p = PCIE_DIP2BUS(cdip);
41483e6495bSDaniel Ice 
41583e6495bSDaniel Ice 	/* Get cdip's MPS/MRRS already setup by pcie_initchild_mps() */
41683e6495bSDaniel Ice 	ASSERT(cdip_bus_p);
41783e6495bSDaniel Ice 	cdip_dev_ctrl  = PCIE_CAP_GET(16, cdip_bus_p, PCIE_DEVCTL);
41883e6495bSDaniel Ice 	cdip_mrrs_mps  = cdip_dev_ctrl &
41983e6495bSDaniel Ice 	    (PCIE_DEVCTL_MAX_READ_REQ_MASK | PCIE_DEVCTL_MAX_PAYLOAD_MASK);
42083e6495bSDaniel Ice 
42183e6495bSDaniel Ice 	/* Locate sdip and set its MPS/MRRS when applicable */
42283e6495bSDaniel Ice 	for (sdip = ddi_get_child(ddi_get_parent(cdip)); sdip;
42383e6495bSDaniel Ice 	    sdip = ddi_get_next_sibling(sdip)) {
42483e6495bSDaniel Ice 		uint16_t sdip_dev_ctrl, sdip_mrrs_mps, cap_ptr;
42583e6495bSDaniel Ice 		uint32_t bus_dev_ven_id;
42683e6495bSDaniel Ice 
42783e6495bSDaniel Ice 		if (sdip == cdip || pci_config_setup(sdip, &cfg_hdl)
42883e6495bSDaniel Ice 		    != DDI_SUCCESS)
42983e6495bSDaniel Ice 			continue;
43083e6495bSDaniel Ice 
43183e6495bSDaniel Ice 		/* must be an Intel 41210 bridge */
43283e6495bSDaniel Ice 		bus_dev_ven_id = pci_config_get32(cfg_hdl, PCI_CONF_VENID);
43383e6495bSDaniel Ice 		if (!PCIEB_IS_41210_BRIDGE(bus_dev_ven_id)) {
43483e6495bSDaniel Ice 			pci_config_teardown(&cfg_hdl);
43583e6495bSDaniel Ice 			continue;
43683e6495bSDaniel Ice 		}
43783e6495bSDaniel Ice 
43883e6495bSDaniel Ice 		if (PCI_CAP_LOCATE(cfg_hdl, PCI_CAP_ID_PCI_E, &cap_ptr)
43983e6495bSDaniel Ice 		    != DDI_SUCCESS) {
44083e6495bSDaniel Ice 			pci_config_teardown(&cfg_hdl);
44183e6495bSDaniel Ice 			continue;
44283e6495bSDaniel Ice 		}
44383e6495bSDaniel Ice 
44483e6495bSDaniel Ice 		/* get sdip's MPS/MRRS to compare to cdip's */
44521922c75SToomas Soome 		sdip_dev_ctrl = PCI_CAP_GET16(cfg_hdl, 0, cap_ptr,
44683e6495bSDaniel Ice 		    PCIE_DEVCTL);
44783e6495bSDaniel Ice 		sdip_mrrs_mps = sdip_dev_ctrl &
44883e6495bSDaniel Ice 		    (PCIE_DEVCTL_MAX_READ_REQ_MASK |
44983e6495bSDaniel Ice 		    PCIE_DEVCTL_MAX_PAYLOAD_MASK);
45083e6495bSDaniel Ice 
45183e6495bSDaniel Ice 		/* if sdip already attached then its MPS/MRRS is configured */
45283e6495bSDaniel Ice 		if (i_ddi_devi_attached(sdip)) {
45383e6495bSDaniel Ice 			ASSERT(sdip_mrrs_mps == cdip_mrrs_mps);
45483e6495bSDaniel Ice 			pci_config_teardown(&cfg_hdl);
45583e6495bSDaniel Ice 			continue;
45683e6495bSDaniel Ice 		}
45783e6495bSDaniel Ice 
45883e6495bSDaniel Ice 		/* otherwise, update sdip's MPS/MRRS if different from cdip's */
45983e6495bSDaniel Ice 		if (sdip_mrrs_mps != cdip_mrrs_mps) {
46083e6495bSDaniel Ice 			sdip_dev_ctrl = (sdip_dev_ctrl &
46183e6495bSDaniel Ice 			    ~(PCIE_DEVCTL_MAX_READ_REQ_MASK |
46283e6495bSDaniel Ice 			    PCIE_DEVCTL_MAX_PAYLOAD_MASK)) | cdip_mrrs_mps;
46383e6495bSDaniel Ice 
464b3d69c05SRobert Mustacchi 			(void) PCI_CAP_PUT16(cfg_hdl, 0, cap_ptr, PCIE_DEVCTL,
46583e6495bSDaniel Ice 			    sdip_dev_ctrl);
46683e6495bSDaniel Ice 		}
46783e6495bSDaniel Ice 
46883e6495bSDaniel Ice 		/*
46983e6495bSDaniel Ice 		 * note: sdip's bus_mps will be updated by
47083e6495bSDaniel Ice 		 * pcie_initchild_mps()
47183e6495bSDaniel Ice 		 */
47283e6495bSDaniel Ice 
47383e6495bSDaniel Ice 		pci_config_teardown(&cfg_hdl);
47483e6495bSDaniel Ice 
47583e6495bSDaniel Ice 		break;
47683e6495bSDaniel Ice 	}
47783e6495bSDaniel Ice }
47883e6495bSDaniel Ice 
479d4bc0535SKrishna Elango static int
pcieb_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)480d4bc0535SKrishna Elango pcieb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
481d4bc0535SKrishna Elango {
482d4bc0535SKrishna Elango 	int			instance;
483d4bc0535SKrishna Elango 	char			device_type[8];
484d4bc0535SKrishna Elango 	pcieb_devstate_t	*pcieb;
485d4bc0535SKrishna Elango 	pcie_bus_t		*bus_p = PCIE_DIP2UPBUS(devi);
486d4bc0535SKrishna Elango 	ddi_acc_handle_t	config_handle = bus_p->bus_cfg_hdl;
487d4bc0535SKrishna Elango 
488d4bc0535SKrishna Elango 	switch (cmd) {
489d4bc0535SKrishna Elango 	case DDI_RESUME:
490d4bc0535SKrishna Elango 		(void) pcie_pwr_resume(devi);
491d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
492d4bc0535SKrishna Elango 
493d4bc0535SKrishna Elango 	default:
494d4bc0535SKrishna Elango 		return (DDI_FAILURE);
495d4bc0535SKrishna Elango 
496d4bc0535SKrishna Elango 	case DDI_ATTACH:
497d4bc0535SKrishna Elango 		break;
498d4bc0535SKrishna Elango 	}
499d4bc0535SKrishna Elango 
500d4bc0535SKrishna Elango 	if (!(PCIE_IS_BDG(bus_p))) {
501d4bc0535SKrishna Elango 		PCIEB_DEBUG(DBG_ATTACH, devi, "This is not a switch or"
502d4bc0535SKrishna Elango 		" bridge\n");
503d4bc0535SKrishna Elango 		return (DDI_FAILURE);
504d4bc0535SKrishna Elango 	}
505d4bc0535SKrishna Elango 
506d4bc0535SKrishna Elango 	/*
507d4bc0535SKrishna Elango 	 * If PCIE_LINKCTL_LINK_DISABLE bit in the PCIe Config
508d4bc0535SKrishna Elango 	 * Space (PCIe Capability Link Control Register) is set,
509d4bc0535SKrishna Elango 	 * then do not bind the driver.
510d4bc0535SKrishna Elango 	 */
511d4bc0535SKrishna Elango 	if (PCIE_CAP_GET(16, bus_p, PCIE_LINKCTL) & PCIE_LINKCTL_LINK_DISABLE)
512d4bc0535SKrishna Elango 		return (DDI_FAILURE);
513d4bc0535SKrishna Elango 
514d4bc0535SKrishna Elango 	/*
515d4bc0535SKrishna Elango 	 * Allocate and get soft state structure.
516d4bc0535SKrishna Elango 	 */
517d4bc0535SKrishna Elango 	instance = ddi_get_instance(devi);
518d4bc0535SKrishna Elango 	if (ddi_soft_state_zalloc(pcieb_state, instance) != DDI_SUCCESS)
519d4bc0535SKrishna Elango 		return (DDI_FAILURE);
520d4bc0535SKrishna Elango 	pcieb = ddi_get_soft_state(pcieb_state, instance);
521d4bc0535SKrishna Elango 	pcieb->pcieb_dip = devi;
522d4bc0535SKrishna Elango 
523d4bc0535SKrishna Elango 	if ((pcieb_fm_init(pcieb)) != DDI_SUCCESS) {
524d4bc0535SKrishna Elango 		PCIEB_DEBUG(DBG_ATTACH, devi, "Failed in pcieb_fm_init\n");
525d4bc0535SKrishna Elango 		goto fail;
526d4bc0535SKrishna Elango 	}
527d4bc0535SKrishna Elango 	pcieb->pcieb_init_flags |= PCIEB_INIT_FM;
528d4bc0535SKrishna Elango 
529d4bc0535SKrishna Elango 	mutex_init(&pcieb->pcieb_mutex, NULL, MUTEX_DRIVER, NULL);
530d4bc0535SKrishna Elango 	mutex_init(&pcieb->pcieb_err_mutex, NULL, MUTEX_DRIVER,
531d4bc0535SKrishna Elango 	    (void *)pcieb->pcieb_fm_ibc);
532d4bc0535SKrishna Elango 	mutex_init(&pcieb->pcieb_peek_poke_mutex, NULL, MUTEX_DRIVER,
533d4bc0535SKrishna Elango 	    (void *)pcieb->pcieb_fm_ibc);
534d4bc0535SKrishna Elango 
535d4bc0535SKrishna Elango 	/* create special properties for device identification */
536d4bc0535SKrishna Elango 	pcieb_id_props(pcieb);
537d4bc0535SKrishna Elango 
538d4bc0535SKrishna Elango 	/*
539d4bc0535SKrishna Elango 	 * Power management setup. This also makes sure that switch/bridge
540d4bc0535SKrishna Elango 	 * is at D0 during attach.
541d4bc0535SKrishna Elango 	 */
542d4bc0535SKrishna Elango 	if (pwr_common_setup(devi) != DDI_SUCCESS) {
543d4bc0535SKrishna Elango 		PCIEB_DEBUG(DBG_PWR, devi, "pwr_common_setup failed\n");
544d4bc0535SKrishna Elango 		goto fail;
545d4bc0535SKrishna Elango 	}
546d4bc0535SKrishna Elango 
547d4bc0535SKrishna Elango 	if (pcieb_pwr_setup(devi) != DDI_SUCCESS) {
548d4bc0535SKrishna Elango 		PCIEB_DEBUG(DBG_PWR, devi, "pxb_pwr_setup failed \n");
549d4bc0535SKrishna Elango 		goto fail;
550d4bc0535SKrishna Elango 	}
551d4bc0535SKrishna Elango 
552d4bc0535SKrishna Elango 	/*
553d4bc0535SKrishna Elango 	 * Make sure the "device_type" property exists.
554d4bc0535SKrishna Elango 	 */
555d4bc0535SKrishna Elango 	if (pcieb_is_pcie_device_type(devi))
556d4bc0535SKrishna Elango 		(void) strcpy(device_type, "pciex");
557d4bc0535SKrishna Elango 	else
558d4bc0535SKrishna Elango 		(void) strcpy(device_type, "pci");
559d4bc0535SKrishna Elango 
560d4bc0535SKrishna Elango 	(void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
561d4bc0535SKrishna Elango 	    "device_type", device_type);
562d4bc0535SKrishna Elango 
563d4bc0535SKrishna Elango 	/*
564d4bc0535SKrishna Elango 	 * Check whether the "ranges" property is present.
565d4bc0535SKrishna Elango 	 * Otherwise create the ranges property by reading
566d4bc0535SKrishna Elango 	 * the configuration registers
567d4bc0535SKrishna Elango 	 */
568d4bc0535SKrishna Elango 	if (ddi_prop_exists(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
569d4bc0535SKrishna Elango 	    "ranges") == 0) {
570d4bc0535SKrishna Elango 		pcieb_create_ranges_prop(devi, config_handle);
571d4bc0535SKrishna Elango 	}
572d4bc0535SKrishna Elango 
573d4bc0535SKrishna Elango 	if (PCIE_IS_PCI_BDG(bus_p))
574d4bc0535SKrishna Elango 		pcieb_set_pci_perf_parameters(devi, config_handle);
575d4bc0535SKrishna Elango 
576d4bc0535SKrishna Elango #ifdef PX_PLX
577d4bc0535SKrishna Elango 	pcieb_attach_plx_workarounds(pcieb);
578d4bc0535SKrishna Elango #endif /* PX_PLX */
579d4bc0535SKrishna Elango 
58026947304SEvan Yan 	if (pcie_init(devi, NULL) != DDI_SUCCESS)
58126947304SEvan Yan 		goto fail;
582d4bc0535SKrishna Elango 
58383e6495bSDaniel Ice 	/* Intel PCIe-to-PCI 41210 bridge workaround -- if applicable */
58483e6495bSDaniel Ice 	if (pcieb_disable_41210_wkarnd == 0 &&
58583e6495bSDaniel Ice 	    PCIEB_IS_41210_BRIDGE(bus_p->bus_dev_ven_id))
58683e6495bSDaniel Ice 		pcieb_41210_mps_wkrnd(devi);
58783e6495bSDaniel Ice 
588d4bc0535SKrishna Elango 	/*
589d4bc0535SKrishna Elango 	 * Initialize interrupt handlers. Ignore return value.
590d4bc0535SKrishna Elango 	 */
591d4bc0535SKrishna Elango 	(void) pcieb_intr_attach(pcieb);
592d4bc0535SKrishna Elango 
59370f83219SEvan Yan 	(void) pcie_hpintr_enable(devi);
59470f83219SEvan Yan 
595b3d69c05SRobert Mustacchi 	(void) pcie_link_bw_enable(devi);
596b3d69c05SRobert Mustacchi 
597d4bc0535SKrishna Elango 	/* Do any platform specific workarounds needed at this time */
598d4bc0535SKrishna Elango 	pcieb_plat_attach_workaround(devi);
599d4bc0535SKrishna Elango 
600d4bc0535SKrishna Elango 	/*
6015b2c4190SRobert Mustacchi 	 * If this is a root port, we need to go through and at this point in
6025b2c4190SRobert Mustacchi 	 * time set up and initialize all fabric-wide settings such as the max
6035b2c4190SRobert Mustacchi 	 * packet size, tagging, etc. Since this will involve scanning the
6045b2c4190SRobert Mustacchi 	 * fabric, all error enabling and sw workarounds should be in place
6055b2c4190SRobert Mustacchi 	 * before doing this. For hotplug-capable bridges, this will happen
6065b2c4190SRobert Mustacchi 	 * again when a hotplug event occurs. See the pcie theory statement in
6075b2c4190SRobert Mustacchi 	 * uts/common/io/pciex/pcie.c for more information.
608d4bc0535SKrishna Elango 	 */
609d4bc0535SKrishna Elango 	if (PCIE_IS_RP(bus_p))
6105b2c4190SRobert Mustacchi 		pcie_fabric_setup(devi);
611d4bc0535SKrishna Elango 
612d4bc0535SKrishna Elango 	ddi_report_dev(devi);
613d4bc0535SKrishna Elango 	return (DDI_SUCCESS);
614d4bc0535SKrishna Elango 
615d4bc0535SKrishna Elango fail:
616d4bc0535SKrishna Elango 	(void) pcieb_detach(devi, DDI_DETACH);
617d4bc0535SKrishna Elango 	return (DDI_FAILURE);
618d4bc0535SKrishna Elango }
619d4bc0535SKrishna Elango 
620d4bc0535SKrishna Elango static int
pcieb_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)621d4bc0535SKrishna Elango pcieb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
622d4bc0535SKrishna Elango {
623d4bc0535SKrishna Elango 	pcieb_devstate_t *pcieb;
624d4bc0535SKrishna Elango 	int error = DDI_SUCCESS;
625d4bc0535SKrishna Elango 
626d4bc0535SKrishna Elango 	switch (cmd) {
627d4bc0535SKrishna Elango 	case DDI_SUSPEND:
628d4bc0535SKrishna Elango 		error = pcie_pwr_suspend(devi);
629d4bc0535SKrishna Elango 		return (error);
630d4bc0535SKrishna Elango 
631d4bc0535SKrishna Elango 	case DDI_DETACH:
632d4bc0535SKrishna Elango 		break;
633d4bc0535SKrishna Elango 
634d4bc0535SKrishna Elango 	default:
635d4bc0535SKrishna Elango 		return (DDI_FAILURE);
636d4bc0535SKrishna Elango 	}
637d4bc0535SKrishna Elango 
638d4bc0535SKrishna Elango 	pcieb = ddi_get_soft_state(pcieb_state, ddi_get_instance(devi));
639d4bc0535SKrishna Elango 
64070f83219SEvan Yan 	/* disable hotplug interrupt */
64170f83219SEvan Yan 	(void) pcie_hpintr_disable(devi);
64270f83219SEvan Yan 
643d4bc0535SKrishna Elango 	/* remove interrupt handlers */
644d4bc0535SKrishna Elango 	pcieb_intr_fini(pcieb);
645d4bc0535SKrishna Elango 
64626947304SEvan Yan 	/* uninitialize inband PCI-E HPC if present */
64726947304SEvan Yan 	(void) pcie_uninit(devi);
648d4bc0535SKrishna Elango 
649d4bc0535SKrishna Elango 	(void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type");
650d4bc0535SKrishna Elango 
651d4bc0535SKrishna Elango 	(void) ndi_prop_remove(DDI_DEV_T_NONE, pcieb->pcieb_dip,
652d4bc0535SKrishna Elango 	    "pcie_ce_mask");
653d4bc0535SKrishna Elango 
654d4bc0535SKrishna Elango 	if (pcieb->pcieb_init_flags & PCIEB_INIT_FM)
655d4bc0535SKrishna Elango 		pcieb_fm_fini(pcieb);
656d4bc0535SKrishna Elango 
657d4bc0535SKrishna Elango 	pcieb_pwr_teardown(devi);
658d4bc0535SKrishna Elango 	pwr_common_teardown(devi);
659d4bc0535SKrishna Elango 
660d4bc0535SKrishna Elango 	mutex_destroy(&pcieb->pcieb_peek_poke_mutex);
661d4bc0535SKrishna Elango 	mutex_destroy(&pcieb->pcieb_err_mutex);
662d4bc0535SKrishna Elango 	mutex_destroy(&pcieb->pcieb_mutex);
663d4bc0535SKrishna Elango 
664d4bc0535SKrishna Elango 	/*
665d4bc0535SKrishna Elango 	 * And finally free the per-pci soft state.
666d4bc0535SKrishna Elango 	 */
667d4bc0535SKrishna Elango 	ddi_soft_state_free(pcieb_state, ddi_get_instance(devi));
668d4bc0535SKrishna Elango 
669d4bc0535SKrishna Elango 	return (DDI_SUCCESS);
670d4bc0535SKrishna Elango }
671d4bc0535SKrishna Elango 
672d4bc0535SKrishna Elango static int
pcieb_bus_map(dev_info_t * dip,dev_info_t * rdip,ddi_map_req_t * mp,off_t offset,off_t len,caddr_t * vaddrp)673d4bc0535SKrishna Elango pcieb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
674d4bc0535SKrishna Elango     off_t offset, off_t len, caddr_t *vaddrp)
675d4bc0535SKrishna Elango {
676d4bc0535SKrishna Elango 	dev_info_t *pdip;
677d4bc0535SKrishna Elango 
6789757e35cSStephen Hanson 	if (PCIE_IS_RP(PCIE_DIP2BUS(dip)) && mp->map_handlep != NULL) {
679837c1ac4SStephen Hanson 		ddi_acc_impl_t *hdlp =
680837c1ac4SStephen Hanson 		    (ddi_acc_impl_t *)(mp->map_handlep)->ah_platform_private;
681837c1ac4SStephen Hanson 
682837c1ac4SStephen Hanson 		pcieb_set_prot_scan(dip, hdlp);
683837c1ac4SStephen Hanson 	}
684d4bc0535SKrishna Elango 	pdip = (dev_info_t *)DEVI(dip)->devi_parent;
685d4bc0535SKrishna Elango 	return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip, rdip, mp,
686d4bc0535SKrishna Elango 	    offset, len, vaddrp));
687d4bc0535SKrishna Elango }
688d4bc0535SKrishna Elango 
689d4bc0535SKrishna Elango static int
pcieb_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)690d4bc0535SKrishna Elango pcieb_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
691d4bc0535SKrishna Elango     void *arg, void *result)
692d4bc0535SKrishna Elango {
693d4bc0535SKrishna Elango 	pci_regspec_t *drv_regp;
694d4bc0535SKrishna Elango 	int	reglen;
695d4bc0535SKrishna Elango 	int	rn;
696d4bc0535SKrishna Elango 	int	totreg;
697d4bc0535SKrishna Elango 	pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state,
698d4bc0535SKrishna Elango 	    ddi_get_instance(dip));
699d4bc0535SKrishna Elango 	struct detachspec *ds;
700d4bc0535SKrishna Elango 	struct attachspec *as;
701d4bc0535SKrishna Elango 
702d4bc0535SKrishna Elango 	switch (ctlop) {
703d4bc0535SKrishna Elango 	case DDI_CTLOPS_REPORTDEV:
704d4bc0535SKrishna Elango 		if (rdip == (dev_info_t *)0)
705d4bc0535SKrishna Elango 			return (DDI_FAILURE);
706fc256490SJason Beloro 
707fc256490SJason Beloro 		if (ddi_get_parent(rdip) == dip) {
708fc256490SJason Beloro 			cmn_err(CE_CONT, "?PCIE-device: %s@%s, %s%d\n",
709fc256490SJason Beloro 			    ddi_node_name(rdip), ddi_get_name_addr(rdip),
710fc256490SJason Beloro 			    ddi_driver_name(rdip), ddi_get_instance(rdip));
711fc256490SJason Beloro 		}
712fc256490SJason Beloro 
713fc256490SJason Beloro 		/* Pass it up for fabric sync */
714fc256490SJason Beloro 		(void) ddi_ctlops(dip, rdip, ctlop, arg, result);
715d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
716d4bc0535SKrishna Elango 
717d4bc0535SKrishna Elango 	case DDI_CTLOPS_INITCHILD:
718d4bc0535SKrishna Elango 		return (pcieb_initchild((dev_info_t *)arg));
719d4bc0535SKrishna Elango 
720d4bc0535SKrishna Elango 	case DDI_CTLOPS_UNINITCHILD:
721d4bc0535SKrishna Elango 		pcieb_uninitchild((dev_info_t *)arg);
722d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
723d4bc0535SKrishna Elango 
724d4bc0535SKrishna Elango 	case DDI_CTLOPS_SIDDEV:
725d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
726d4bc0535SKrishna Elango 
727d4bc0535SKrishna Elango 	case DDI_CTLOPS_REGSIZE:
728d4bc0535SKrishna Elango 	case DDI_CTLOPS_NREGS:
729d4bc0535SKrishna Elango 		if (rdip == (dev_info_t *)0)
730d4bc0535SKrishna Elango 			return (DDI_FAILURE);
731d4bc0535SKrishna Elango 		break;
732d4bc0535SKrishna Elango 
733d4bc0535SKrishna Elango 	case DDI_CTLOPS_PEEK:
734d4bc0535SKrishna Elango 	case DDI_CTLOPS_POKE:
735d4bc0535SKrishna Elango 		return (pcieb_plat_peekpoke(dip, rdip, ctlop, arg, result));
736d4bc0535SKrishna Elango 	case DDI_CTLOPS_ATTACH:
737d4bc0535SKrishna Elango 		if (!pcie_is_child(dip, rdip))
738d4bc0535SKrishna Elango 			return (DDI_SUCCESS);
739d4bc0535SKrishna Elango 
740d4bc0535SKrishna Elango 		as = (struct attachspec *)arg;
741d4bc0535SKrishna Elango 		switch (as->when) {
742d4bc0535SKrishna Elango 		case DDI_PRE:
743d4bc0535SKrishna Elango 			if (as->cmd == DDI_RESUME) {
744d4bc0535SKrishna Elango 				pcie_clear_errors(rdip);
745d4bc0535SKrishna Elango 				if (pcieb_plat_ctlops(rdip, ctlop, arg) !=
746d4bc0535SKrishna Elango 				    DDI_SUCCESS)
747d4bc0535SKrishna Elango 					return (DDI_FAILURE);
748d4bc0535SKrishna Elango 			}
749d4bc0535SKrishna Elango 
750d4bc0535SKrishna Elango 			if (as->cmd == DDI_ATTACH)
751d4bc0535SKrishna Elango 				return (pcie_pm_hold(dip));
752d4bc0535SKrishna Elango 
753d4bc0535SKrishna Elango 			return (DDI_SUCCESS);
754d4bc0535SKrishna Elango 
755d4bc0535SKrishna Elango 		case DDI_POST:
756c2cc6e07SRamesh Chitrothu 			if (as->cmd == DDI_ATTACH &&
757c2cc6e07SRamesh Chitrothu 			    as->result != DDI_SUCCESS) {
758c2cc6e07SRamesh Chitrothu 				/*
759c2cc6e07SRamesh Chitrothu 				 * Attach failed for the child device. The child
760c2cc6e07SRamesh Chitrothu 				 * driver may have made PM calls before the
761c2cc6e07SRamesh Chitrothu 				 * attach failed. pcie_pm_remove_child() should
762c2cc6e07SRamesh Chitrothu 				 * cleanup PM state and holds (if any)
763c2cc6e07SRamesh Chitrothu 				 * associated with the child device.
764c2cc6e07SRamesh Chitrothu 				 */
765c2cc6e07SRamesh Chitrothu 				return (pcie_pm_remove_child(dip, rdip));
766c2cc6e07SRamesh Chitrothu 			}
767d4bc0535SKrishna Elango 
768d4bc0535SKrishna Elango 			if (as->result == DDI_SUCCESS) {
769d4bc0535SKrishna Elango 				pf_init(rdip, (void *)pcieb->pcieb_fm_ibc,
770d4bc0535SKrishna Elango 				    as->cmd);
771d4bc0535SKrishna Elango 
772d4bc0535SKrishna Elango 				(void) pcieb_plat_ctlops(rdip, ctlop, arg);
773d4bc0535SKrishna Elango 			}
774d4bc0535SKrishna Elango 
775d4bc0535SKrishna Elango 			/*
776d4bc0535SKrishna Elango 			 * For empty hotplug-capable slots, we should explicitly
777d4bc0535SKrishna Elango 			 * disable the errors, so that we won't panic upon
778d4bc0535SKrishna Elango 			 * unsupported hotplug messages.
779d4bc0535SKrishna Elango 			 */
780d4bc0535SKrishna Elango 			if ((!ddi_prop_exists(DDI_DEV_T_ANY, rdip,
781d4bc0535SKrishna Elango 			    DDI_PROP_DONTPASS, "hotplug-capable")) ||
782d4bc0535SKrishna Elango 			    ddi_get_child(rdip)) {
783d4bc0535SKrishna Elango 				(void) pcie_postattach_child(rdip);
784d4bc0535SKrishna Elango 				return (DDI_SUCCESS);
785d4bc0535SKrishna Elango 			}
786d4bc0535SKrishna Elango 
787d4bc0535SKrishna Elango 			pcie_disable_errors(rdip);
788d4bc0535SKrishna Elango 
789d4bc0535SKrishna Elango 			return (DDI_SUCCESS);
790d4bc0535SKrishna Elango 		default:
791d4bc0535SKrishna Elango 			break;
792d4bc0535SKrishna Elango 		}
793d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
794d4bc0535SKrishna Elango 
795d4bc0535SKrishna Elango 	case DDI_CTLOPS_DETACH:
796d4bc0535SKrishna Elango 		if (!pcie_is_child(dip, rdip))
797d4bc0535SKrishna Elango 			return (DDI_SUCCESS);
798d4bc0535SKrishna Elango 
799d4bc0535SKrishna Elango 		ds = (struct detachspec *)arg;
800d4bc0535SKrishna Elango 		switch (ds->when) {
801d4bc0535SKrishna Elango 		case DDI_PRE:
802d4bc0535SKrishna Elango 			pf_fini(rdip, ds->cmd);
803d4bc0535SKrishna Elango 			return (DDI_SUCCESS);
804d4bc0535SKrishna Elango 
805d4bc0535SKrishna Elango 		case DDI_POST:
806d4bc0535SKrishna Elango 			if (pcieb_plat_ctlops(rdip, ctlop, arg) != DDI_SUCCESS)
807d4bc0535SKrishna Elango 				return (DDI_FAILURE);
808d4bc0535SKrishna Elango 			if (ds->cmd == DDI_DETACH &&
809d4bc0535SKrishna Elango 			    ds->result == DDI_SUCCESS) {
810d4bc0535SKrishna Elango 				return (pcie_pm_remove_child(dip, rdip));
811d4bc0535SKrishna Elango 			}
812d4bc0535SKrishna Elango 			return (DDI_SUCCESS);
813d4bc0535SKrishna Elango 		default:
814d4bc0535SKrishna Elango 			break;
815d4bc0535SKrishna Elango 		}
816d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
817d4bc0535SKrishna Elango 	default:
818d4bc0535SKrishna Elango 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
819d4bc0535SKrishna Elango 	}
820d4bc0535SKrishna Elango 
821d4bc0535SKrishna Elango 	*(int *)result = 0;
822d4bc0535SKrishna Elango 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip,
823d4bc0535SKrishna Elango 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg", (caddr_t)&drv_regp,
824d4bc0535SKrishna Elango 	    &reglen) != DDI_SUCCESS)
825d4bc0535SKrishna Elango 		return (DDI_FAILURE);
826d4bc0535SKrishna Elango 
827d4bc0535SKrishna Elango 	totreg = reglen / sizeof (pci_regspec_t);
828d4bc0535SKrishna Elango 	if (ctlop == DDI_CTLOPS_NREGS)
829d4bc0535SKrishna Elango 		*(int *)result = totreg;
830d4bc0535SKrishna Elango 	else if (ctlop == DDI_CTLOPS_REGSIZE) {
831d4bc0535SKrishna Elango 		rn = *(int *)arg;
832d4bc0535SKrishna Elango 		if (rn >= totreg) {
833d4bc0535SKrishna Elango 			kmem_free(drv_regp, reglen);
834d4bc0535SKrishna Elango 			return (DDI_FAILURE);
835d4bc0535SKrishna Elango 		}
836d4bc0535SKrishna Elango 
837d4bc0535SKrishna Elango 		*(off_t *)result = drv_regp[rn].pci_size_low |
838d4bc0535SKrishna Elango 		    ((uint64_t)drv_regp[rn].pci_size_hi << 32);
839d4bc0535SKrishna Elango 	}
840d4bc0535SKrishna Elango 
841d4bc0535SKrishna Elango 	kmem_free(drv_regp, reglen);
842d4bc0535SKrishna Elango 	return (DDI_SUCCESS);
843d4bc0535SKrishna Elango }
844d4bc0535SKrishna Elango 
845d4bc0535SKrishna Elango /*
846d4bc0535SKrishna Elango  * name_child
847d4bc0535SKrishna Elango  *
848d4bc0535SKrishna Elango  * This function is called from init_child to name a node. It is
849d4bc0535SKrishna Elango  * also passed as a callback for node merging functions.
850d4bc0535SKrishna Elango  *
851d4bc0535SKrishna Elango  * return value: DDI_SUCCESS, DDI_FAILURE
852d4bc0535SKrishna Elango  */
853d4bc0535SKrishna Elango static int
pcieb_name_child(dev_info_t * child,char * name,int namelen)854d4bc0535SKrishna Elango pcieb_name_child(dev_info_t *child, char *name, int namelen)
855d4bc0535SKrishna Elango {
856d4bc0535SKrishna Elango 	pci_regspec_t *pci_rp;
85726947304SEvan Yan 	uint_t device, func;
858d4bc0535SKrishna Elango 	char **unit_addr;
859d4bc0535SKrishna Elango 	uint_t n;
860d4bc0535SKrishna Elango 
861d4bc0535SKrishna Elango 	/*
862d4bc0535SKrishna Elango 	 * For .conf nodes, use unit-address property as name
863d4bc0535SKrishna Elango 	 */
864d4bc0535SKrishna Elango 	if (ndi_dev_is_persistent_node(child) == 0) {
865d4bc0535SKrishna Elango 		if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
866d4bc0535SKrishna Elango 		    DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) !=
867d4bc0535SKrishna Elango 		    DDI_PROP_SUCCESS) {
868d4bc0535SKrishna Elango 			cmn_err(CE_WARN,
869d4bc0535SKrishna Elango 			    "cannot find unit-address in %s.conf",
870d4bc0535SKrishna Elango 			    ddi_driver_name(child));
871d4bc0535SKrishna Elango 			return (DDI_FAILURE);
872d4bc0535SKrishna Elango 		}
873d4bc0535SKrishna Elango 		if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
874d4bc0535SKrishna Elango 			cmn_err(CE_WARN, "unit-address property in %s.conf"
875d4bc0535SKrishna Elango 			    " not well-formed", ddi_driver_name(child));
876d4bc0535SKrishna Elango 			ddi_prop_free(unit_addr);
877d4bc0535SKrishna Elango 			return (DDI_FAILURE);
878d4bc0535SKrishna Elango 		}
879d4bc0535SKrishna Elango 		(void) snprintf(name, namelen, "%s", *unit_addr);
880d4bc0535SKrishna Elango 		ddi_prop_free(unit_addr);
881d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
882d4bc0535SKrishna Elango 	}
883d4bc0535SKrishna Elango 
884d4bc0535SKrishna Elango 	/*
885d4bc0535SKrishna Elango 	 * Get the address portion of the node name based on
886d4bc0535SKrishna Elango 	 * the function and device number.
887d4bc0535SKrishna Elango 	 */
888d4bc0535SKrishna Elango 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
889d4bc0535SKrishna Elango 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) {
890d4bc0535SKrishna Elango 		return (DDI_FAILURE);
891d4bc0535SKrishna Elango 	}
892d4bc0535SKrishna Elango 
893d4bc0535SKrishna Elango 	/* copy the device identifications */
89426947304SEvan Yan 	device = PCI_REG_DEV_G(pci_rp[0].pci_phys_hi);
895d4bc0535SKrishna Elango 	func = PCI_REG_FUNC_G(pci_rp[0].pci_phys_hi);
896d4bc0535SKrishna Elango 
89726947304SEvan Yan 	if (pcie_ari_is_enabled(ddi_get_parent(child))
89826947304SEvan Yan 	    == PCIE_ARI_FORW_ENABLED) {
89926947304SEvan Yan 		func = (device << 3) | func;
90026947304SEvan Yan 		device = 0;
90126947304SEvan Yan 	}
90226947304SEvan Yan 
903d4bc0535SKrishna Elango 	if (func != 0)
90426947304SEvan Yan 		(void) snprintf(name, namelen, "%x,%x", device, func);
905d4bc0535SKrishna Elango 	else
90626947304SEvan Yan 		(void) snprintf(name, namelen, "%x", device);
907d4bc0535SKrishna Elango 
908d4bc0535SKrishna Elango 	ddi_prop_free(pci_rp);
909d4bc0535SKrishna Elango 	return (DDI_SUCCESS);
910d4bc0535SKrishna Elango }
911d4bc0535SKrishna Elango 
912d4bc0535SKrishna Elango static int
pcieb_initchild(dev_info_t * child)913d4bc0535SKrishna Elango pcieb_initchild(dev_info_t *child)
914d4bc0535SKrishna Elango {
915d4bc0535SKrishna Elango 	char name[MAXNAMELEN];
916d4bc0535SKrishna Elango 	int result = DDI_FAILURE;
917d4bc0535SKrishna Elango 	pcieb_devstate_t *pcieb =
918d4bc0535SKrishna Elango 	    (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state,
919d4bc0535SKrishna Elango 	    ddi_get_instance(ddi_get_parent(child)));
920d4bc0535SKrishna Elango 
921d4bc0535SKrishna Elango 	/*
922d4bc0535SKrishna Elango 	 * Name the child
923d4bc0535SKrishna Elango 	 */
924d4bc0535SKrishna Elango 	if (pcieb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS) {
925d4bc0535SKrishna Elango 		result = DDI_FAILURE;
926d4bc0535SKrishna Elango 		goto done;
927d4bc0535SKrishna Elango 	}
928d4bc0535SKrishna Elango 	ddi_set_name_addr(child, name);
929d4bc0535SKrishna Elango 
930d4bc0535SKrishna Elango 	/*
931d4bc0535SKrishna Elango 	 * Pseudo nodes indicate a prototype node with per-instance
932d4bc0535SKrishna Elango 	 * properties to be merged into the real h/w device node.
933d4bc0535SKrishna Elango 	 * The interpretation of the unit-address is DD[,F]
934d4bc0535SKrishna Elango 	 * where DD is the device id and F is the function.
935d4bc0535SKrishna Elango 	 */
936d4bc0535SKrishna Elango 	if (ndi_dev_is_persistent_node(child) == 0) {
937d4bc0535SKrishna Elango 		extern int pci_allow_pseudo_children;
938d4bc0535SKrishna Elango 
939d4bc0535SKrishna Elango 		/*
940d4bc0535SKrishna Elango 		 * Try to merge the properties from this prototype
941d4bc0535SKrishna Elango 		 * node into real h/w nodes.
942d4bc0535SKrishna Elango 		 */
9432e98bdabSvitezslav batrla - Sun Microsystems - Prague Czech Republic 		if (ndi_merge_node(child, pcieb_name_child) == DDI_SUCCESS) {
944d4bc0535SKrishna Elango 			/*
945d4bc0535SKrishna Elango 			 * Merged ok - return failure to remove the node.
946d4bc0535SKrishna Elango 			 */
947d4bc0535SKrishna Elango 			ddi_set_name_addr(child, NULL);
948d4bc0535SKrishna Elango 			result = DDI_FAILURE;
949d4bc0535SKrishna Elango 			goto done;
950d4bc0535SKrishna Elango 		}
951d4bc0535SKrishna Elango 
952d4bc0535SKrishna Elango 		/* workaround for ddivs to run under PCI-E */
953d4bc0535SKrishna Elango 		if (pci_allow_pseudo_children) {
954d4bc0535SKrishna Elango 			result = DDI_SUCCESS;
955d4bc0535SKrishna Elango 			goto done;
956d4bc0535SKrishna Elango 		}
957d4bc0535SKrishna Elango 
958d4bc0535SKrishna Elango 		/*
959d4bc0535SKrishna Elango 		 * The child was not merged into a h/w node,
960d4bc0535SKrishna Elango 		 * but there's not much we can do with it other
961d4bc0535SKrishna Elango 		 * than return failure to cause the node to be removed.
962d4bc0535SKrishna Elango 		 */
963d4bc0535SKrishna Elango 		cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
964d4bc0535SKrishna Elango 		    ddi_driver_name(child), ddi_get_name_addr(child),
965d4bc0535SKrishna Elango 		    ddi_driver_name(child));
966d4bc0535SKrishna Elango 		ddi_set_name_addr(child, NULL);
967d4bc0535SKrishna Elango 		result = DDI_NOT_WELL_FORMED;
968d4bc0535SKrishna Elango 		goto done;
969d4bc0535SKrishna Elango 	}
970d4bc0535SKrishna Elango 
971d4bc0535SKrishna Elango 	/* platform specific initchild */
972d4bc0535SKrishna Elango 	pcieb_plat_initchild(child);
973d4bc0535SKrishna Elango 
974d4bc0535SKrishna Elango 	if (pcie_pm_hold(pcieb->pcieb_dip) != DDI_SUCCESS) {
975d4bc0535SKrishna Elango 		PCIEB_DEBUG(DBG_PWR, pcieb->pcieb_dip,
976d4bc0535SKrishna Elango 		    "INITCHILD: px_pm_hold failed\n");
977d4bc0535SKrishna Elango 		result = DDI_FAILURE;
978d4bc0535SKrishna Elango 		goto done;
979d4bc0535SKrishna Elango 	}
980d4bc0535SKrishna Elango 	/* Any return from here must call pcie_pm_release */
981d4bc0535SKrishna Elango 
982d4bc0535SKrishna Elango 	/*
983d4bc0535SKrishna Elango 	 * If configuration registers were previously saved by
984d4bc0535SKrishna Elango 	 * child (before it entered D3), then let the child do the
985d4bc0535SKrishna Elango 	 * restore to set up the config regs as it'll first need to
986d4bc0535SKrishna Elango 	 * power the device out of D3.
987d4bc0535SKrishna Elango 	 */
988d4bc0535SKrishna Elango 	if (ddi_prop_exists(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
989d4bc0535SKrishna Elango 	    "config-regs-saved-by-child") == 1) {
990d4bc0535SKrishna Elango 		PCIEB_DEBUG(DBG_PWR, ddi_get_parent(child),
991d4bc0535SKrishna Elango 		    "INITCHILD: config regs to be restored by child"
992d4bc0535SKrishna Elango 		    " for %s@%s\n", ddi_node_name(child),
993d4bc0535SKrishna Elango 		    ddi_get_name_addr(child));
994d4bc0535SKrishna Elango 
995d4bc0535SKrishna Elango 		result = DDI_SUCCESS;
996d4bc0535SKrishna Elango 		goto cleanup;
997d4bc0535SKrishna Elango 	}
998d4bc0535SKrishna Elango 
999d4bc0535SKrishna Elango 	PCIEB_DEBUG(DBG_PWR, ddi_get_parent(child),
1000d4bc0535SKrishna Elango 	    "INITCHILD: config regs setup for %s@%s\n",
1001d4bc0535SKrishna Elango 	    ddi_node_name(child), ddi_get_name_addr(child));
1002d4bc0535SKrishna Elango 
1003fc256490SJason Beloro 	pcie_init_dom(child);
1004fc256490SJason Beloro 
1005c0da6274SZhi-Jun Robin Fu 	if (pcie_initchild(child) != DDI_SUCCESS) {
1006d4bc0535SKrishna Elango 		result = DDI_FAILURE;
1007fc256490SJason Beloro 		pcie_fini_dom(child);
1008d4bc0535SKrishna Elango 		goto cleanup;
1009d4bc0535SKrishna Elango 	}
1010d4bc0535SKrishna Elango 
1011d4bc0535SKrishna Elango #ifdef PX_PLX
1012d4bc0535SKrishna Elango 	if (pcieb_init_plx_workarounds(pcieb, child) == DDI_FAILURE) {
1013d4bc0535SKrishna Elango 		result = DDI_FAILURE;
1014fc256490SJason Beloro 		pcie_fini_dom(child);
1015d4bc0535SKrishna Elango 		goto cleanup;
1016d4bc0535SKrishna Elango 	}
1017d4bc0535SKrishna Elango #endif /* PX_PLX */
1018d4bc0535SKrishna Elango 
1019d4bc0535SKrishna Elango 	result = DDI_SUCCESS;
1020d4bc0535SKrishna Elango cleanup:
1021d4bc0535SKrishna Elango 	pcie_pm_release(pcieb->pcieb_dip);
1022d4bc0535SKrishna Elango done:
1023d4bc0535SKrishna Elango 	return (result);
1024d4bc0535SKrishna Elango }
1025d4bc0535SKrishna Elango 
1026d4bc0535SKrishna Elango static void
pcieb_uninitchild(dev_info_t * dip)1027d4bc0535SKrishna Elango pcieb_uninitchild(dev_info_t *dip)
1028d4bc0535SKrishna Elango {
1029d4bc0535SKrishna Elango 
1030d4bc0535SKrishna Elango 	pcie_uninitchild(dip);
1031d4bc0535SKrishna Elango 
1032d4bc0535SKrishna Elango 	pcieb_plat_uninitchild(dip);
1033d4bc0535SKrishna Elango 
1034d4bc0535SKrishna Elango 	ddi_set_name_addr(dip, NULL);
1035d4bc0535SKrishna Elango 
1036d4bc0535SKrishna Elango 	/*
1037d4bc0535SKrishna Elango 	 * Strip the node to properly convert it back to prototype form
1038d4bc0535SKrishna Elango 	 */
1039d4bc0535SKrishna Elango 	ddi_remove_minor_node(dip, NULL);
1040d4bc0535SKrishna Elango 
1041d4bc0535SKrishna Elango 	ddi_prop_remove_all(dip);
1042d4bc0535SKrishna Elango }
1043d4bc0535SKrishna Elango 
1044d4bc0535SKrishna Elango static boolean_t
pcieb_is_pcie_device_type(dev_info_t * dip)1045d4bc0535SKrishna Elango pcieb_is_pcie_device_type(dev_info_t *dip)
1046d4bc0535SKrishna Elango {
1047d4bc0535SKrishna Elango 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
1048d4bc0535SKrishna Elango 
1049d4bc0535SKrishna Elango 	if (PCIE_IS_SW(bus_p) || PCIE_IS_RP(bus_p) || PCIE_IS_PCI2PCIE(bus_p))
1050d4bc0535SKrishna Elango 		return (B_TRUE);
1051d4bc0535SKrishna Elango 
1052d4bc0535SKrishna Elango 	return (B_FALSE);
1053d4bc0535SKrishna Elango }
1054d4bc0535SKrishna Elango 
1055d4bc0535SKrishna Elango static int
pcieb_intr_attach(pcieb_devstate_t * pcieb)1056d4bc0535SKrishna Elango pcieb_intr_attach(pcieb_devstate_t *pcieb)
1057d4bc0535SKrishna Elango {
1058d4bc0535SKrishna Elango 	int			intr_types;
1059d4bc0535SKrishna Elango 	dev_info_t		*dip = pcieb->pcieb_dip;
1060d4bc0535SKrishna Elango 
1061d4bc0535SKrishna Elango 	/* Allow platform specific code to do any initialization first */
1062d4bc0535SKrishna Elango 	pcieb_plat_intr_attach(pcieb);
1063d4bc0535SKrishna Elango 
1064d4bc0535SKrishna Elango 	/*
1065d4bc0535SKrishna Elango 	 * Initialize interrupt handlers.
1066d4bc0535SKrishna Elango 	 * If both MSI and FIXED are supported, try to attach MSI first.
1067d4bc0535SKrishna Elango 	 * If MSI fails for any reason, then try FIXED, but only allow one
1068d4bc0535SKrishna Elango 	 * type to be attached.
1069d4bc0535SKrishna Elango 	 */
1070d4bc0535SKrishna Elango 	if (ddi_intr_get_supported_types(dip, &intr_types) != DDI_SUCCESS) {
1071d4bc0535SKrishna Elango 		PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_get_supported_types"
1072d4bc0535SKrishna Elango 		    " failed\n");
1073d4bc0535SKrishna Elango 		goto FAIL;
1074d4bc0535SKrishna Elango 	}
1075d4bc0535SKrishna Elango 
1076d4bc0535SKrishna Elango 	if ((intr_types & DDI_INTR_TYPE_MSI) &&
1077d4bc0535SKrishna Elango 	    (pcieb_msi_supported(dip) == DDI_SUCCESS)) {
1078d4bc0535SKrishna Elango 		if (pcieb_intr_init(pcieb, DDI_INTR_TYPE_MSI) == DDI_SUCCESS)
1079d4bc0535SKrishna Elango 			intr_types = DDI_INTR_TYPE_MSI;
1080d4bc0535SKrishna Elango 		else {
1081d4bc0535SKrishna Elango 			PCIEB_DEBUG(DBG_ATTACH, dip, "Unable to attach MSI"
1082d4bc0535SKrishna Elango 			    " handler\n");
1083d4bc0535SKrishna Elango 		}
1084d4bc0535SKrishna Elango 	}
1085d4bc0535SKrishna Elango 
1086d4bc0535SKrishna Elango 	if (intr_types != DDI_INTR_TYPE_MSI) {
1087d4bc0535SKrishna Elango 		/*
1088d4bc0535SKrishna Elango 		 * MSIs are not supported or MSI initialization failed. For Root
1089d4bc0535SKrishna Elango 		 * Ports mark this so error handling might try to fallback to
1090d4bc0535SKrishna Elango 		 * some other mechanism if available (machinecheck etc.).
1091d4bc0535SKrishna Elango 		 */
1092d4bc0535SKrishna Elango 		if (PCIE_IS_RP(PCIE_DIP2UPBUS(dip)))
1093d4bc0535SKrishna Elango 			pcieb->pcieb_no_aer_msi = B_TRUE;
1094d4bc0535SKrishna Elango 	}
1095d4bc0535SKrishna Elango 
1096d4bc0535SKrishna Elango 	if (intr_types & DDI_INTR_TYPE_FIXED) {
1097d4bc0535SKrishna Elango 		if (pcieb_intr_init(pcieb, DDI_INTR_TYPE_FIXED) !=
1098d4bc0535SKrishna Elango 		    DDI_SUCCESS) {
1099d4bc0535SKrishna Elango 			PCIEB_DEBUG(DBG_ATTACH, dip,
1100d4bc0535SKrishna Elango 			    "Unable to attach INTx handler\n");
1101d4bc0535SKrishna Elango 			goto FAIL;
1102d4bc0535SKrishna Elango 		}
1103d4bc0535SKrishna Elango 	}
1104d4bc0535SKrishna Elango 	return (DDI_SUCCESS);
1105d4bc0535SKrishna Elango 
1106d4bc0535SKrishna Elango FAIL:
1107d4bc0535SKrishna Elango 	return (DDI_FAILURE);
1108d4bc0535SKrishna Elango }
1109d4bc0535SKrishna Elango 
1110d4bc0535SKrishna Elango /*
1111d4bc0535SKrishna Elango  * This function initializes internally generated interrupts only.
1112d4bc0535SKrishna Elango  * It does not affect any interrupts generated by downstream devices
1113d4bc0535SKrishna Elango  * or the forwarding of them.
1114d4bc0535SKrishna Elango  *
1115d4bc0535SKrishna Elango  * Enable Device Specific Interrupts or Hotplug features here.
1116d4bc0535SKrishna Elango  * Enabling features may change how many interrupts are requested
1117d4bc0535SKrishna Elango  * by the device.  If features are not enabled first, the
1118d4bc0535SKrishna Elango  * device might not ask for any interrupts.
1119d4bc0535SKrishna Elango  */
1120d4bc0535SKrishna Elango static int
pcieb_intr_init(pcieb_devstate_t * pcieb,int intr_type)1121d4bc0535SKrishna Elango pcieb_intr_init(pcieb_devstate_t *pcieb, int intr_type)
1122d4bc0535SKrishna Elango {
1123d4bc0535SKrishna Elango 	dev_info_t	*dip = pcieb->pcieb_dip;
1124d4bc0535SKrishna Elango 	int		nintrs, request, count, x;
1125d4bc0535SKrishna Elango 	int		intr_cap = 0;
1126d4bc0535SKrishna Elango 	int		inum = 0;
1127b3d69c05SRobert Mustacchi 	int		ret;
1128d4bc0535SKrishna Elango 	pcie_bus_t	*bus_p = PCIE_DIP2UPBUS(dip);
1129d4bc0535SKrishna Elango 	uint16_t	vendorid = bus_p->bus_dev_ven_id & 0xFFFF;
1130d4bc0535SKrishna Elango 	boolean_t	is_hp = B_FALSE;
1131d4bc0535SKrishna Elango 	boolean_t	is_pme = B_FALSE;
1132b3d69c05SRobert Mustacchi 	boolean_t	is_lbw = B_FALSE;
1133d4bc0535SKrishna Elango 
1134d4bc0535SKrishna Elango 	PCIEB_DEBUG(DBG_ATTACH, dip, "pcieb_intr_init: Attaching %s handler\n",
1135d4bc0535SKrishna Elango 	    (intr_type == DDI_INTR_TYPE_MSI) ? "MSI" : "INTx");
1136d4bc0535SKrishna Elango 
1137d4bc0535SKrishna Elango 	request = 0;
113826947304SEvan Yan 	if (PCIE_IS_HOTPLUG_ENABLED(dip)) {
1139d4bc0535SKrishna Elango 		is_hp = B_TRUE;
1140d4bc0535SKrishna Elango 	}
1141d4bc0535SKrishna Elango 
1142d4bc0535SKrishna Elango 	if ((intr_type == DDI_INTR_TYPE_MSI) && PCIE_IS_RP(bus_p) &&
1143d4bc0535SKrishna Elango 	    (vendorid == NVIDIA_VENDOR_ID)) {
1144d4bc0535SKrishna Elango 		is_pme = B_TRUE;
1145b3d69c05SRobert Mustacchi 	}
1146b3d69c05SRobert Mustacchi 
1147b3d69c05SRobert Mustacchi 	if (intr_type == DDI_INTR_TYPE_MSI && pcie_link_bw_supported(dip)) {
1148b3d69c05SRobert Mustacchi 		is_lbw = B_TRUE;
1149d4bc0535SKrishna Elango 	}
1150d4bc0535SKrishna Elango 
1151d4bc0535SKrishna Elango 	/*
1152b3d69c05SRobert Mustacchi 	 * The hot-plug, link bandwidth, and power management events all are
1153b3d69c05SRobert Mustacchi 	 * based on the PCI Express capability. Therefore, they all share their
1154b3d69c05SRobert Mustacchi 	 * own interrupt.
1155b3d69c05SRobert Mustacchi 	 */
1156b3d69c05SRobert Mustacchi 	if (is_hp || is_pme || is_lbw) {
1157b3d69c05SRobert Mustacchi 		request++;
1158b3d69c05SRobert Mustacchi 	}
1159b3d69c05SRobert Mustacchi 
1160b3d69c05SRobert Mustacchi 	/*
1161b3d69c05SRobert Mustacchi 	 * If this device is a root port, which means it can have MSI interrupts
1162b3d69c05SRobert Mustacchi 	 * enabled for AERs, then we need to request one.
1163d4bc0535SKrishna Elango 	 */
1164d4bc0535SKrishna Elango 	if (intr_type == DDI_INTR_TYPE_MSI) {
1165b3d69c05SRobert Mustacchi 		if (PCIE_IS_RP(bus_p) && PCIE_HAS_AER(bus_p)) {
1166d4bc0535SKrishna Elango 			request++;
1167b3d69c05SRobert Mustacchi 		}
1168d4bc0535SKrishna Elango 	}
1169d4bc0535SKrishna Elango 
1170d4bc0535SKrishna Elango 	if (request == 0)
1171d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
1172d4bc0535SKrishna Elango 
1173d4bc0535SKrishna Elango 	/*
1174d4bc0535SKrishna Elango 	 * Get number of supported interrupts.
1175d4bc0535SKrishna Elango 	 *
1176d4bc0535SKrishna Elango 	 * Several Bridges/Switches will not have this property set, resulting
1177d4bc0535SKrishna Elango 	 * in a FAILURE, if the device is not configured in a way that
1178d4bc0535SKrishna Elango 	 * interrupts are needed. (eg. hotplugging)
1179d4bc0535SKrishna Elango 	 */
1180d4bc0535SKrishna Elango 	ret = ddi_intr_get_nintrs(dip, intr_type, &nintrs);
1181d4bc0535SKrishna Elango 	if ((ret != DDI_SUCCESS) || (nintrs == 0)) {
1182d4bc0535SKrishna Elango 		PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_get_nintrs ret:%d"
1183d4bc0535SKrishna Elango 		    " req:%d\n", ret, nintrs);
1184d4bc0535SKrishna Elango 		return (DDI_FAILURE);
1185d4bc0535SKrishna Elango 	}
1186d4bc0535SKrishna Elango 
1187d4bc0535SKrishna Elango 	PCIEB_DEBUG(DBG_ATTACH, dip, "bdf 0x%x: ddi_intr_get_nintrs: nintrs %d",
1188d4bc0535SKrishna Elango 	    " request %d\n", bus_p->bus_bdf, nintrs, request);
1189d4bc0535SKrishna Elango 
1190d4bc0535SKrishna Elango 	if (request > nintrs)
1191d4bc0535SKrishna Elango 		request = nintrs;
1192d4bc0535SKrishna Elango 
1193d4bc0535SKrishna Elango 	/* Allocate an array of interrupt handlers */
1194d4bc0535SKrishna Elango 	pcieb->pcieb_htable_size = sizeof (ddi_intr_handle_t) * request;
1195d4bc0535SKrishna Elango 	pcieb->pcieb_htable = kmem_zalloc(pcieb->pcieb_htable_size,
1196d4bc0535SKrishna Elango 	    KM_SLEEP);
1197d4bc0535SKrishna Elango 	pcieb->pcieb_init_flags |= PCIEB_INIT_HTABLE;
1198d4bc0535SKrishna Elango 
1199d4bc0535SKrishna Elango 	ret = ddi_intr_alloc(dip, pcieb->pcieb_htable, intr_type, inum,
1200d4bc0535SKrishna Elango 	    request, &count, DDI_INTR_ALLOC_NORMAL);
1201d4bc0535SKrishna Elango 	if ((ret != DDI_SUCCESS) || (count == 0)) {
1202d4bc0535SKrishna Elango 		PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_alloc() ret: %d ask: %d"
1203d4bc0535SKrishna Elango 		    " actual: %d\n", ret, request, count);
1204d4bc0535SKrishna Elango 		goto FAIL;
1205d4bc0535SKrishna Elango 	}
1206d4bc0535SKrishna Elango 	pcieb->pcieb_init_flags |= PCIEB_INIT_ALLOC;
1207d4bc0535SKrishna Elango 
1208d4bc0535SKrishna Elango 	/* Save the actual number of interrupts allocated */
1209d4bc0535SKrishna Elango 	pcieb->pcieb_intr_count = count;
1210d4bc0535SKrishna Elango 	if (count < request) {
1211d4bc0535SKrishna Elango 		PCIEB_DEBUG(DBG_ATTACH, dip, "bdf 0%x: Requested Intr: %d"
1212d4bc0535SKrishna Elango 		    " Received: %d\n", bus_p->bus_bdf, request, count);
1213d4bc0535SKrishna Elango 	}
1214d4bc0535SKrishna Elango 
1215d4bc0535SKrishna Elango 	/*
1216d4bc0535SKrishna Elango 	 * NVidia (MCP55 and other) chipsets have a errata that if the number
1217d4bc0535SKrishna Elango 	 * of requested MSI intrs is not allocated we have to fall back to INTx.
1218d4bc0535SKrishna Elango 	 */
1219d4bc0535SKrishna Elango 	if (intr_type == DDI_INTR_TYPE_MSI) {
1220d4bc0535SKrishna Elango 		if (PCIE_IS_RP(bus_p) && (vendorid == NVIDIA_VENDOR_ID)) {
1221d4bc0535SKrishna Elango 			if (request != count)
1222d4bc0535SKrishna Elango 				goto FAIL;
1223d4bc0535SKrishna Elango 		}
1224d4bc0535SKrishna Elango 	}
1225d4bc0535SKrishna Elango 
1226d4bc0535SKrishna Elango 	/* Get interrupt priority */
1227d4bc0535SKrishna Elango 	ret = ddi_intr_get_pri(pcieb->pcieb_htable[0],
1228d4bc0535SKrishna Elango 	    &pcieb->pcieb_intr_priority);
1229d4bc0535SKrishna Elango 	if (ret != DDI_SUCCESS) {
1230d4bc0535SKrishna Elango 		PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_get_pri() ret: %d\n",
1231d4bc0535SKrishna Elango 		    ret);
1232d4bc0535SKrishna Elango 		goto FAIL;
1233d4bc0535SKrishna Elango 	}
1234d4bc0535SKrishna Elango 
1235d4bc0535SKrishna Elango 	if (pcieb->pcieb_intr_priority >= LOCK_LEVEL) {
1236d4bc0535SKrishna Elango 		pcieb->pcieb_intr_priority = LOCK_LEVEL - 1;
1237d4bc0535SKrishna Elango 		ret = ddi_intr_set_pri(pcieb->pcieb_htable[0],
1238d4bc0535SKrishna Elango 		    pcieb->pcieb_intr_priority);
1239d4bc0535SKrishna Elango 		if (ret != DDI_SUCCESS) {
1240d4bc0535SKrishna Elango 			PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_set_pri() ret:"
1241d4bc0535SKrishna Elango 			" %d\n", ret);
1242d4bc0535SKrishna Elango 
1243d4bc0535SKrishna Elango 			goto FAIL;
1244d4bc0535SKrishna Elango 		}
1245d4bc0535SKrishna Elango 	}
1246d4bc0535SKrishna Elango 
1247d4bc0535SKrishna Elango 	mutex_init(&pcieb->pcieb_intr_mutex, NULL, MUTEX_DRIVER, NULL);
1248d4bc0535SKrishna Elango 
1249d4bc0535SKrishna Elango 	pcieb->pcieb_init_flags |= PCIEB_INIT_MUTEX;
1250d4bc0535SKrishna Elango 
1251d4bc0535SKrishna Elango 	for (count = 0; count < pcieb->pcieb_intr_count; count++) {
1252d4bc0535SKrishna Elango 		ret = ddi_intr_add_handler(pcieb->pcieb_htable[count],
1253d4bc0535SKrishna Elango 		    pcieb_intr_handler, (caddr_t)pcieb,
1254d4bc0535SKrishna Elango 		    (caddr_t)(uintptr_t)(inum + count));
1255d4bc0535SKrishna Elango 
1256d4bc0535SKrishna Elango 		if (ret != DDI_SUCCESS) {
1257d4bc0535SKrishna Elango 			PCIEB_DEBUG(DBG_ATTACH, dip, "Cannot add "
1258d4bc0535SKrishna Elango 			    "interrupt(%d)\n", ret);
1259d4bc0535SKrishna Elango 			break;
1260d4bc0535SKrishna Elango 		}
1261d4bc0535SKrishna Elango 	}
1262d4bc0535SKrishna Elango 
1263d4bc0535SKrishna Elango 	/* If unsucessful, remove the added handlers */
1264d4bc0535SKrishna Elango 	if (ret != DDI_SUCCESS) {
1265d4bc0535SKrishna Elango 		for (x = 0; x < count; x++) {
1266d4bc0535SKrishna Elango 			(void) ddi_intr_remove_handler(pcieb->pcieb_htable[x]);
1267d4bc0535SKrishna Elango 		}
1268d4bc0535SKrishna Elango 		goto FAIL;
1269d4bc0535SKrishna Elango 	}
1270d4bc0535SKrishna Elango 
1271d4bc0535SKrishna Elango 	pcieb->pcieb_init_flags |= PCIEB_INIT_HANDLER;
1272d4bc0535SKrishna Elango 
1273d4bc0535SKrishna Elango 	(void) ddi_intr_get_cap(pcieb->pcieb_htable[0], &intr_cap);
1274d4bc0535SKrishna Elango 
1275d4bc0535SKrishna Elango 	/*
1276d4bc0535SKrishna Elango 	 * Get this intr lock because we are not quite ready to handle
1277d4bc0535SKrishna Elango 	 * interrupts immediately after enabling it. The MSI multi register
1278d4bc0535SKrishna Elango 	 * gets programmed in ddi_intr_enable after which we need to get the
1279d4bc0535SKrishna Elango 	 * MSI offsets for Hotplug/AER.
1280d4bc0535SKrishna Elango 	 */
1281d4bc0535SKrishna Elango 	mutex_enter(&pcieb->pcieb_intr_mutex);
1282d4bc0535SKrishna Elango 
1283d4bc0535SKrishna Elango 	if (intr_cap & DDI_INTR_FLAG_BLOCK) {
1284d4bc0535SKrishna Elango 		(void) ddi_intr_block_enable(pcieb->pcieb_htable,
1285d4bc0535SKrishna Elango 		    pcieb->pcieb_intr_count);
1286d4bc0535SKrishna Elango 		pcieb->pcieb_init_flags |= PCIEB_INIT_BLOCK;
1287d4bc0535SKrishna Elango 	} else {
1288d4bc0535SKrishna Elango 		for (count = 0; count < pcieb->pcieb_intr_count; count++) {
1289d4bc0535SKrishna Elango 			(void) ddi_intr_enable(pcieb->pcieb_htable[count]);
1290d4bc0535SKrishna Elango 		}
1291d4bc0535SKrishna Elango 	}
1292d4bc0535SKrishna Elango 	pcieb->pcieb_init_flags |= PCIEB_INIT_ENABLE;
1293d4bc0535SKrishna Elango 
1294d4bc0535SKrishna Elango 	/* Save the interrupt type */
1295d4bc0535SKrishna Elango 	pcieb->pcieb_intr_type = intr_type;
1296d4bc0535SKrishna Elango 
1297d4bc0535SKrishna Elango 	/* Get the MSI offset for hotplug/PME from the PCIe cap reg */
1298d4bc0535SKrishna Elango 	if (intr_type == DDI_INTR_TYPE_MSI) {
1299b3d69c05SRobert Mustacchi 		uint16_t pcie_msi_off;
1300b3d69c05SRobert Mustacchi 		pcie_msi_off = PCI_CAP_GET16(bus_p->bus_cfg_hdl, 0,
1301d4bc0535SKrishna Elango 		    bus_p->bus_pcie_off, PCIE_PCIECAP) &
1302d4bc0535SKrishna Elango 		    PCIE_PCIECAP_INT_MSG_NUM;
1303d4bc0535SKrishna Elango 
1304b3d69c05SRobert Mustacchi 		if (pcie_msi_off >= count) {
1305b3d69c05SRobert Mustacchi 			PCIEB_DEBUG(DBG_ATTACH, dip, "MSI number %u in PCIe "
1306b3d69c05SRobert Mustacchi 			    "cap > max allocated %d\n", pcie_msi_off, count);
1307d4bc0535SKrishna Elango 			mutex_exit(&pcieb->pcieb_intr_mutex);
1308d4bc0535SKrishna Elango 			goto FAIL;
1309d4bc0535SKrishna Elango 		}
1310d4bc0535SKrishna Elango 
1311b3d69c05SRobert Mustacchi 		if (is_hp) {
1312b3d69c05SRobert Mustacchi 			pcieb->pcieb_isr_tab[pcie_msi_off] |= PCIEB_INTR_SRC_HP;
1313b3d69c05SRobert Mustacchi 		}
1314b3d69c05SRobert Mustacchi 
1315b3d69c05SRobert Mustacchi 		if (is_pme) {
1316b3d69c05SRobert Mustacchi 			pcieb->pcieb_isr_tab[pcie_msi_off] |=
1317b3d69c05SRobert Mustacchi 			    PCIEB_INTR_SRC_PME;
1318b3d69c05SRobert Mustacchi 		}
1319d4bc0535SKrishna Elango 
1320b3d69c05SRobert Mustacchi 		if (is_lbw) {
1321b3d69c05SRobert Mustacchi 			pcieb->pcieb_isr_tab[pcie_msi_off] |=
1322b3d69c05SRobert Mustacchi 			    PCIEB_INTR_SRC_LBW;
1323b3d69c05SRobert Mustacchi 		}
1324d4bc0535SKrishna Elango 	} else {
1325d4bc0535SKrishna Elango 		/* INTx handles only Hotplug interrupts */
1326d4bc0535SKrishna Elango 		if (is_hp)
1327d4bc0535SKrishna Elango 			pcieb->pcieb_isr_tab[0] |= PCIEB_INTR_SRC_HP;
1328d4bc0535SKrishna Elango 	}
1329d4bc0535SKrishna Elango 
1330d4bc0535SKrishna Elango 
1331d4bc0535SKrishna Elango 	/*
1332d4bc0535SKrishna Elango 	 * Get the MSI offset for errors from the AER Root Error status
1333d4bc0535SKrishna Elango 	 * register.
1334d4bc0535SKrishna Elango 	 */
1335d4bc0535SKrishna Elango 	if ((intr_type == DDI_INTR_TYPE_MSI) && PCIE_IS_RP(bus_p)) {
1336d4bc0535SKrishna Elango 		if (PCIE_HAS_AER(bus_p)) {
1337d4bc0535SKrishna Elango 			int aer_msi_off;
133821922c75SToomas Soome 			aer_msi_off = (PCI_XCAP_GET32(bus_p->bus_cfg_hdl, 0,
1339d4bc0535SKrishna Elango 			    bus_p->bus_aer_off, PCIE_AER_RE_STS) >>
1340d4bc0535SKrishna Elango 			    PCIE_AER_RE_STS_MSG_NUM_SHIFT) &
1341d4bc0535SKrishna Elango 			    PCIE_AER_RE_STS_MSG_NUM_MASK;
1342d4bc0535SKrishna Elango 
1343d4bc0535SKrishna Elango 			if (aer_msi_off >= count) {
1344d4bc0535SKrishna Elango 				PCIEB_DEBUG(DBG_ATTACH, dip, "MSI number %d in"
1345d4bc0535SKrishna Elango 				    " AER cap > max allocated %d\n",
1346d4bc0535SKrishna Elango 				    aer_msi_off, count);
1347d4bc0535SKrishna Elango 				mutex_exit(&pcieb->pcieb_intr_mutex);
1348d4bc0535SKrishna Elango 				goto FAIL;
1349d4bc0535SKrishna Elango 			}
1350d4bc0535SKrishna Elango 			pcieb->pcieb_isr_tab[aer_msi_off] |= PCIEB_INTR_SRC_AER;
1351d4bc0535SKrishna Elango 		} else {
1352d4bc0535SKrishna Elango 			/*
1353d4bc0535SKrishna Elango 			 * This RP does not have AER. Fallback to the
1354d4bc0535SKrishna Elango 			 * SERR+Machinecheck approach if available.
1355d4bc0535SKrishna Elango 			 */
1356d4bc0535SKrishna Elango 			pcieb->pcieb_no_aer_msi = B_TRUE;
1357d4bc0535SKrishna Elango 		}
1358d4bc0535SKrishna Elango 	}
1359d4bc0535SKrishna Elango 
1360d4bc0535SKrishna Elango 	mutex_exit(&pcieb->pcieb_intr_mutex);
1361d4bc0535SKrishna Elango 	return (DDI_SUCCESS);
1362d4bc0535SKrishna Elango 
1363d4bc0535SKrishna Elango FAIL:
13640ff3af34SShesha Sreenivasamurthy 	pcieb_intr_fini(pcieb);
1365d4bc0535SKrishna Elango 	return (DDI_FAILURE);
1366d4bc0535SKrishna Elango }
1367d4bc0535SKrishna Elango 
1368d4bc0535SKrishna Elango static void
pcieb_intr_fini(pcieb_devstate_t * pcieb)1369d4bc0535SKrishna Elango pcieb_intr_fini(pcieb_devstate_t *pcieb)
1370d4bc0535SKrishna Elango {
1371d4bc0535SKrishna Elango 	int x;
1372d4bc0535SKrishna Elango 	int count = pcieb->pcieb_intr_count;
1373d4bc0535SKrishna Elango 	int flags = pcieb->pcieb_init_flags;
1374d4bc0535SKrishna Elango 
1375d4bc0535SKrishna Elango 	if ((flags & PCIEB_INIT_ENABLE) &&
1376d4bc0535SKrishna Elango 	    (flags & PCIEB_INIT_BLOCK)) {
1377d4bc0535SKrishna Elango 		(void) ddi_intr_block_disable(pcieb->pcieb_htable, count);
1378d4bc0535SKrishna Elango 		flags &= ~(PCIEB_INIT_ENABLE |
1379d4bc0535SKrishna Elango 		    PCIEB_INIT_BLOCK);
1380d4bc0535SKrishna Elango 	}
1381d4bc0535SKrishna Elango 
1382d4bc0535SKrishna Elango 	if (flags & PCIEB_INIT_MUTEX)
1383d4bc0535SKrishna Elango 		mutex_destroy(&pcieb->pcieb_intr_mutex);
1384d4bc0535SKrishna Elango 
1385d4bc0535SKrishna Elango 	for (x = 0; x < count; x++) {
1386d4bc0535SKrishna Elango 		if (flags & PCIEB_INIT_ENABLE)
1387d4bc0535SKrishna Elango 			(void) ddi_intr_disable(pcieb->pcieb_htable[x]);
1388d4bc0535SKrishna Elango 
1389d4bc0535SKrishna Elango 		if (flags & PCIEB_INIT_HANDLER)
1390d4bc0535SKrishna Elango 			(void) ddi_intr_remove_handler(pcieb->pcieb_htable[x]);
1391d4bc0535SKrishna Elango 
1392d4bc0535SKrishna Elango 		if (flags & PCIEB_INIT_ALLOC)
1393d4bc0535SKrishna Elango 			(void) ddi_intr_free(pcieb->pcieb_htable[x]);
1394d4bc0535SKrishna Elango 	}
1395d4bc0535SKrishna Elango 
1396d4bc0535SKrishna Elango 	flags &= ~(PCIEB_INIT_ENABLE | PCIEB_INIT_HANDLER | PCIEB_INIT_ALLOC |
1397d4bc0535SKrishna Elango 	    PCIEB_INIT_MUTEX);
1398d4bc0535SKrishna Elango 
1399d4bc0535SKrishna Elango 	if (flags & PCIEB_INIT_HTABLE)
1400d4bc0535SKrishna Elango 		kmem_free(pcieb->pcieb_htable, pcieb->pcieb_htable_size);
1401d4bc0535SKrishna Elango 
1402d4bc0535SKrishna Elango 	flags &= ~PCIEB_INIT_HTABLE;
1403d4bc0535SKrishna Elango 
1404d4bc0535SKrishna Elango 	pcieb->pcieb_init_flags &= flags;
1405d4bc0535SKrishna Elango }
1406d4bc0535SKrishna Elango 
1407d4bc0535SKrishna Elango /*
1408d4bc0535SKrishna Elango  * Checks if this device needs MSIs enabled or not.
1409d4bc0535SKrishna Elango  */
1410d4bc0535SKrishna Elango /*ARGSUSED*/
1411d4bc0535SKrishna Elango static int
pcieb_msi_supported(dev_info_t * dip)1412d4bc0535SKrishna Elango pcieb_msi_supported(dev_info_t *dip)
1413d4bc0535SKrishna Elango {
1414d4bc0535SKrishna Elango 	return ((pcieb_enable_msi && pcieb_plat_msi_supported(dip)) ?
1415d4bc0535SKrishna Elango 	    DDI_SUCCESS: DDI_FAILURE);
1416d4bc0535SKrishna Elango }
1417d4bc0535SKrishna Elango 
1418d4bc0535SKrishna Elango /*ARGSUSED*/
14190ff3af34SShesha Sreenivasamurthy static int
pcieb_fm_init_child(dev_info_t * dip,dev_info_t * tdip,int cap,ddi_iblock_cookie_t * ibc)1420d4bc0535SKrishna Elango pcieb_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap,
1421d4bc0535SKrishna Elango     ddi_iblock_cookie_t *ibc)
1422d4bc0535SKrishna Elango {
1423d4bc0535SKrishna Elango 	pcieb_devstate_t  *pcieb = ddi_get_soft_state(pcieb_state,
1424d4bc0535SKrishna Elango 	    ddi_get_instance(dip));
1425d4bc0535SKrishna Elango 
1426d4bc0535SKrishna Elango 	ASSERT(ibc != NULL);
1427d4bc0535SKrishna Elango 	*ibc = pcieb->pcieb_fm_ibc;
1428d4bc0535SKrishna Elango 
1429d4bc0535SKrishna Elango 	return (DEVI(dip)->devi_fmhdl->fh_cap | DDI_FM_ACCCHK_CAPABLE |
1430d4bc0535SKrishna Elango 	    DDI_FM_DMACHK_CAPABLE);
1431d4bc0535SKrishna Elango }
1432d4bc0535SKrishna Elango 
1433d4bc0535SKrishna Elango static int
pcieb_fm_init(pcieb_devstate_t * pcieb_p)1434d4bc0535SKrishna Elango pcieb_fm_init(pcieb_devstate_t *pcieb_p)
1435d4bc0535SKrishna Elango {
1436d4bc0535SKrishna Elango 	dev_info_t	*dip = pcieb_p->pcieb_dip;
1437d4bc0535SKrishna Elango 	int		fm_cap = DDI_FM_EREPORT_CAPABLE;
1438d4bc0535SKrishna Elango 
1439d4bc0535SKrishna Elango 	/*
1440d4bc0535SKrishna Elango 	 * Request our capability level and get our parents capability
1441d4bc0535SKrishna Elango 	 * and ibc.
1442d4bc0535SKrishna Elango 	 */
1443d4bc0535SKrishna Elango 	ddi_fm_init(dip, &fm_cap, &pcieb_p->pcieb_fm_ibc);
1444d4bc0535SKrishna Elango 
1445d4bc0535SKrishna Elango 	return (DDI_SUCCESS);
1446d4bc0535SKrishna Elango }
1447d4bc0535SKrishna Elango 
1448d4bc0535SKrishna Elango /*
1449d4bc0535SKrishna Elango  * Breakdown our FMA resources
1450d4bc0535SKrishna Elango  */
1451d4bc0535SKrishna Elango static void
pcieb_fm_fini(pcieb_devstate_t * pcieb_p)1452d4bc0535SKrishna Elango pcieb_fm_fini(pcieb_devstate_t *pcieb_p)
1453d4bc0535SKrishna Elango {
1454d4bc0535SKrishna Elango 	/*
1455d4bc0535SKrishna Elango 	 * Clean up allocated fm structures
1456d4bc0535SKrishna Elango 	 */
1457d4bc0535SKrishna Elango 	ddi_fm_fini(pcieb_p->pcieb_dip);
1458d4bc0535SKrishna Elango }
1459d4bc0535SKrishna Elango 
1460d4bc0535SKrishna Elango static int
pcieb_open(dev_t * devp,int flags,int otyp,cred_t * credp)1461d4bc0535SKrishna Elango pcieb_open(dev_t *devp, int flags, int otyp, cred_t *credp)
1462d4bc0535SKrishna Elango {
146326947304SEvan Yan 	int		inst = PCI_MINOR_NUM_TO_INSTANCE(getminor(*devp));
146426947304SEvan Yan 	pcieb_devstate_t	*pcieb = ddi_get_soft_state(pcieb_state, inst);
146526947304SEvan Yan 	int	rv;
1466d4bc0535SKrishna Elango 
146726947304SEvan Yan 	if (pcieb == NULL)
1468d4bc0535SKrishna Elango 		return (ENXIO);
1469d4bc0535SKrishna Elango 
147026947304SEvan Yan 	mutex_enter(&pcieb->pcieb_mutex);
147126947304SEvan Yan 	rv = pcie_open(pcieb->pcieb_dip, devp, flags, otyp, credp);
147226947304SEvan Yan 	mutex_exit(&pcieb->pcieb_mutex);
1473d4bc0535SKrishna Elango 
147426947304SEvan Yan 	return (rv);
1475d4bc0535SKrishna Elango }
1476d4bc0535SKrishna Elango 
1477d4bc0535SKrishna Elango static int
pcieb_close(dev_t dev,int flags,int otyp,cred_t * credp)1478d4bc0535SKrishna Elango pcieb_close(dev_t dev, int flags, int otyp, cred_t *credp)
1479d4bc0535SKrishna Elango {
148026947304SEvan Yan 	int		inst = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
148126947304SEvan Yan 	pcieb_devstate_t	*pcieb = ddi_get_soft_state(pcieb_state, inst);
148226947304SEvan Yan 	int	rv;
1483d4bc0535SKrishna Elango 
148426947304SEvan Yan 	if (pcieb == NULL)
1485d4bc0535SKrishna Elango 		return (ENXIO);
1486d4bc0535SKrishna Elango 
148726947304SEvan Yan 	mutex_enter(&pcieb->pcieb_mutex);
148826947304SEvan Yan 	rv = pcie_close(pcieb->pcieb_dip, dev, flags, otyp, credp);
148926947304SEvan Yan 	mutex_exit(&pcieb->pcieb_mutex);
1490d4bc0535SKrishna Elango 
149126947304SEvan Yan 	return (rv);
1492d4bc0535SKrishna Elango }
1493d4bc0535SKrishna Elango 
1494b3d69c05SRobert Mustacchi static int
pcieb_ioctl_retrain(pcieb_devstate_t * pcieb,cred_t * credp)1495b3d69c05SRobert Mustacchi pcieb_ioctl_retrain(pcieb_devstate_t *pcieb, cred_t *credp)
1496b3d69c05SRobert Mustacchi {
1497b3d69c05SRobert Mustacchi 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(pcieb->pcieb_dip);
1498b3d69c05SRobert Mustacchi 
1499b3d69c05SRobert Mustacchi 	if (drv_priv(credp) != 0) {
1500b3d69c05SRobert Mustacchi 		return (EPERM);
1501b3d69c05SRobert Mustacchi 	}
1502b3d69c05SRobert Mustacchi 
1503b3d69c05SRobert Mustacchi 	if (!PCIE_IS_PCIE(bus_p)) {
1504b3d69c05SRobert Mustacchi 		return (ENOTSUP);
1505b3d69c05SRobert Mustacchi 	}
1506b3d69c05SRobert Mustacchi 
1507b3d69c05SRobert Mustacchi 	if (!PCIE_IS_RP(bus_p) && !PCIE_IS_SWD(bus_p)) {
1508b3d69c05SRobert Mustacchi 		return (ENOTSUP);
1509b3d69c05SRobert Mustacchi 	}
1510b3d69c05SRobert Mustacchi 
1511b3d69c05SRobert Mustacchi 	return (pcie_link_retrain(pcieb->pcieb_dip));
1512b3d69c05SRobert Mustacchi }
1513b3d69c05SRobert Mustacchi 
1514b3d69c05SRobert Mustacchi static int
pcieb_ioctl_get_speed(pcieb_devstate_t * pcieb,intptr_t arg,int mode,cred_t * credp)1515b3d69c05SRobert Mustacchi pcieb_ioctl_get_speed(pcieb_devstate_t *pcieb, intptr_t arg, int mode,
1516b3d69c05SRobert Mustacchi     cred_t *credp)
1517b3d69c05SRobert Mustacchi {
1518b3d69c05SRobert Mustacchi 	pcie_bus_t			*bus_p = PCIE_DIP2BUS(pcieb->pcieb_dip);
1519b3d69c05SRobert Mustacchi 	pcieb_ioctl_target_speed_t	pits;
1520b3d69c05SRobert Mustacchi 
1521b3d69c05SRobert Mustacchi 	if (drv_priv(credp) != 0) {
1522b3d69c05SRobert Mustacchi 		return (EPERM);
1523b3d69c05SRobert Mustacchi 	}
1524b3d69c05SRobert Mustacchi 
1525b3d69c05SRobert Mustacchi 	if (!PCIE_IS_PCIE(bus_p)) {
1526b3d69c05SRobert Mustacchi 		return (ENOTSUP);
1527b3d69c05SRobert Mustacchi 	}
1528b3d69c05SRobert Mustacchi 
1529b3d69c05SRobert Mustacchi 	if (!PCIE_IS_RP(bus_p) && !PCIE_IS_SWD(bus_p)) {
1530b3d69c05SRobert Mustacchi 		return (ENOTSUP);
1531b3d69c05SRobert Mustacchi 	}
1532b3d69c05SRobert Mustacchi 
1533b3d69c05SRobert Mustacchi 	pits.pits_flags = 0;
1534b3d69c05SRobert Mustacchi 	pits.pits_speed = PCIEB_LINK_SPEED_UNKNOWN;
1535b3d69c05SRobert Mustacchi 
1536b3d69c05SRobert Mustacchi 	mutex_enter(&bus_p->bus_speed_mutex);
1537b3d69c05SRobert Mustacchi 	if ((bus_p->bus_speed_flags & PCIE_LINK_F_ADMIN_TARGET) != 0) {
1538b3d69c05SRobert Mustacchi 		pits.pits_flags |= PCIEB_FLAGS_ADMIN_SET;
1539b3d69c05SRobert Mustacchi 	}
1540b3d69c05SRobert Mustacchi 	switch (bus_p->bus_target_speed) {
1541b3d69c05SRobert Mustacchi 	case PCIE_LINK_SPEED_2_5:
1542b3d69c05SRobert Mustacchi 		pits.pits_speed = PCIEB_LINK_SPEED_GEN1;
1543b3d69c05SRobert Mustacchi 		break;
1544b3d69c05SRobert Mustacchi 	case PCIE_LINK_SPEED_5:
1545b3d69c05SRobert Mustacchi 		pits.pits_speed = PCIEB_LINK_SPEED_GEN2;
1546b3d69c05SRobert Mustacchi 		break;
1547b3d69c05SRobert Mustacchi 	case PCIE_LINK_SPEED_8:
1548b3d69c05SRobert Mustacchi 		pits.pits_speed = PCIEB_LINK_SPEED_GEN3;
1549b3d69c05SRobert Mustacchi 		break;
1550b3d69c05SRobert Mustacchi 	case PCIE_LINK_SPEED_16:
1551b3d69c05SRobert Mustacchi 		pits.pits_speed = PCIEB_LINK_SPEED_GEN4;
1552b3d69c05SRobert Mustacchi 		break;
155389427192SRobert Mustacchi 	case PCIE_LINK_SPEED_32:
155489427192SRobert Mustacchi 		pits.pits_speed = PCIEB_LINK_SPEED_GEN5;
155589427192SRobert Mustacchi 		break;
155689427192SRobert Mustacchi 	case PCIE_LINK_SPEED_64:
155789427192SRobert Mustacchi 		pits.pits_speed = PCIEB_LINK_SPEED_GEN6;
155889427192SRobert Mustacchi 		break;
1559b3d69c05SRobert Mustacchi 	default:
1560b3d69c05SRobert Mustacchi 		pits.pits_speed = PCIEB_LINK_SPEED_UNKNOWN;
1561b3d69c05SRobert Mustacchi 		break;
1562b3d69c05SRobert Mustacchi 	}
1563b3d69c05SRobert Mustacchi 	mutex_exit(&bus_p->bus_speed_mutex);
1564b3d69c05SRobert Mustacchi 
1565b3d69c05SRobert Mustacchi 	if (ddi_copyout(&pits, (void *)arg, sizeof (pits),
1566b3d69c05SRobert Mustacchi 	    mode & FKIOCTL) != 0) {
1567b3d69c05SRobert Mustacchi 		return (EFAULT);
1568b3d69c05SRobert Mustacchi 	}
1569b3d69c05SRobert Mustacchi 
1570b3d69c05SRobert Mustacchi 	return (0);
1571b3d69c05SRobert Mustacchi }
1572b3d69c05SRobert Mustacchi 
1573b3d69c05SRobert Mustacchi static int
pcieb_ioctl_set_speed(pcieb_devstate_t * pcieb,intptr_t arg,int mode,cred_t * credp)1574b3d69c05SRobert Mustacchi pcieb_ioctl_set_speed(pcieb_devstate_t *pcieb, intptr_t arg, int mode,
1575b3d69c05SRobert Mustacchi     cred_t *credp)
1576b3d69c05SRobert Mustacchi {
1577b3d69c05SRobert Mustacchi 	pcie_bus_t			*bus_p = PCIE_DIP2BUS(pcieb->pcieb_dip);
1578b3d69c05SRobert Mustacchi 	pcieb_ioctl_target_speed_t	pits;
1579b3d69c05SRobert Mustacchi 	pcie_link_speed_t		speed;
1580b3d69c05SRobert Mustacchi 
1581b3d69c05SRobert Mustacchi 	if (drv_priv(credp) != 0) {
1582b3d69c05SRobert Mustacchi 		return (EPERM);
1583b3d69c05SRobert Mustacchi 	}
1584b3d69c05SRobert Mustacchi 
1585b3d69c05SRobert Mustacchi 	if (!PCIE_IS_PCIE(bus_p)) {
1586b3d69c05SRobert Mustacchi 		return (ENOTSUP);
1587b3d69c05SRobert Mustacchi 	}
1588b3d69c05SRobert Mustacchi 
1589b3d69c05SRobert Mustacchi 	if (!PCIE_IS_RP(bus_p) && !PCIE_IS_SWD(bus_p)) {
1590b3d69c05SRobert Mustacchi 		return (ENOTSUP);
1591b3d69c05SRobert Mustacchi 	}
1592b3d69c05SRobert Mustacchi 
1593b3d69c05SRobert Mustacchi 	if (ddi_copyin((void *)arg, &pits, sizeof (pits),
1594b3d69c05SRobert Mustacchi 	    mode & FKIOCTL) != 0) {
1595b3d69c05SRobert Mustacchi 		return (EFAULT);
1596b3d69c05SRobert Mustacchi 	}
1597b3d69c05SRobert Mustacchi 
1598b3d69c05SRobert Mustacchi 	if (pits.pits_flags != 0) {
1599b3d69c05SRobert Mustacchi 		return (EINVAL);
1600b3d69c05SRobert Mustacchi 	}
1601b3d69c05SRobert Mustacchi 
1602b3d69c05SRobert Mustacchi 	switch (pits.pits_speed) {
1603b3d69c05SRobert Mustacchi 	case PCIEB_LINK_SPEED_GEN1:
1604b3d69c05SRobert Mustacchi 		speed = PCIE_LINK_SPEED_2_5;
1605b3d69c05SRobert Mustacchi 		break;
1606b3d69c05SRobert Mustacchi 	case PCIEB_LINK_SPEED_GEN2:
1607b3d69c05SRobert Mustacchi 		speed = PCIE_LINK_SPEED_5;
1608b3d69c05SRobert Mustacchi 		break;
1609b3d69c05SRobert Mustacchi 	case PCIEB_LINK_SPEED_GEN3:
1610b3d69c05SRobert Mustacchi 		speed = PCIE_LINK_SPEED_8;
1611b3d69c05SRobert Mustacchi 		break;
1612b3d69c05SRobert Mustacchi 	case PCIEB_LINK_SPEED_GEN4:
1613b3d69c05SRobert Mustacchi 		speed = PCIE_LINK_SPEED_16;
1614b3d69c05SRobert Mustacchi 		break;
161589427192SRobert Mustacchi 	case PCIEB_LINK_SPEED_GEN5:
161689427192SRobert Mustacchi 		speed = PCIE_LINK_SPEED_32;
161789427192SRobert Mustacchi 		break;
161889427192SRobert Mustacchi 	case PCIEB_LINK_SPEED_GEN6:
161989427192SRobert Mustacchi 		speed = PCIE_LINK_SPEED_64;
162089427192SRobert Mustacchi 		break;
1621b3d69c05SRobert Mustacchi 	default:
1622b3d69c05SRobert Mustacchi 		return (EINVAL);
1623b3d69c05SRobert Mustacchi 	}
1624b3d69c05SRobert Mustacchi 
1625b3d69c05SRobert Mustacchi 	return (pcie_link_set_target(pcieb->pcieb_dip, speed));
1626b3d69c05SRobert Mustacchi }
1627b3d69c05SRobert Mustacchi 
1628d4bc0535SKrishna Elango static int
pcieb_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)1629d4bc0535SKrishna Elango pcieb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
163021922c75SToomas Soome     int *rvalp)
1631d4bc0535SKrishna Elango {
163226947304SEvan Yan 	int		inst = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
163326947304SEvan Yan 	pcieb_devstate_t	*pcieb = ddi_get_soft_state(pcieb_state, inst);
163426947304SEvan Yan 	int		rv;
1635d4bc0535SKrishna Elango 
163626947304SEvan Yan 	if (pcieb == NULL)
1637d4bc0535SKrishna Elango 		return (ENXIO);
1638d4bc0535SKrishna Elango 
1639b3d69c05SRobert Mustacchi 	/*
1640b3d69c05SRobert Mustacchi 	 * Check if this is one of the commands that the bridge driver natively
1641b3d69c05SRobert Mustacchi 	 * understands. There are only a handful of such private ioctls defined
1642b3d69c05SRobert Mustacchi 	 * in pcieb_ioctl.h. Otherwise, this ioctl should be handled by the
1643b3d69c05SRobert Mustacchi 	 * general pcie driver.
1644b3d69c05SRobert Mustacchi 	 */
1645b3d69c05SRobert Mustacchi 	switch (cmd) {
1646b3d69c05SRobert Mustacchi 	case PCIEB_IOCTL_RETRAIN:
1647b3d69c05SRobert Mustacchi 		rv = pcieb_ioctl_retrain(pcieb, credp);
1648b3d69c05SRobert Mustacchi 		break;
1649b3d69c05SRobert Mustacchi 	case PCIEB_IOCTL_GET_TARGET_SPEED:
1650b3d69c05SRobert Mustacchi 		rv = pcieb_ioctl_get_speed(pcieb, arg, mode, credp);
1651b3d69c05SRobert Mustacchi 		break;
1652b3d69c05SRobert Mustacchi 	case PCIEB_IOCTL_SET_TARGET_SPEED:
1653b3d69c05SRobert Mustacchi 		rv = pcieb_ioctl_set_speed(pcieb, arg, mode, credp);
1654b3d69c05SRobert Mustacchi 		break;
1655b3d69c05SRobert Mustacchi 	default:
1656b3d69c05SRobert Mustacchi 		/* To handle devctl and hotplug related ioctls */
1657b3d69c05SRobert Mustacchi 		rv = pcie_ioctl(pcieb->pcieb_dip, dev, cmd, arg, mode, credp,
1658b3d69c05SRobert Mustacchi 		    rvalp);
1659b3d69c05SRobert Mustacchi 		break;
1660b3d69c05SRobert Mustacchi 	}
1661d4bc0535SKrishna Elango 
166226947304SEvan Yan 	return (rv);
1663d4bc0535SKrishna Elango }
1664d4bc0535SKrishna Elango 
1665d4bc0535SKrishna Elango /*
1666d4bc0535SKrishna Elango  * Common interrupt handler for hotplug, PME and errors.
1667d4bc0535SKrishna Elango  */
1668d4bc0535SKrishna Elango static uint_t
pcieb_intr_handler(caddr_t arg1,caddr_t arg2)1669d4bc0535SKrishna Elango pcieb_intr_handler(caddr_t arg1, caddr_t arg2)
1670d4bc0535SKrishna Elango {
1671d4bc0535SKrishna Elango 	pcieb_devstate_t *pcieb_p = (pcieb_devstate_t *)arg1;
1672d4bc0535SKrishna Elango 	dev_info_t	*dip = pcieb_p->pcieb_dip;
1673d4bc0535SKrishna Elango 	ddi_fm_error_t	derr;
1674d4bc0535SKrishna Elango 	int		sts = 0;
1675d4bc0535SKrishna Elango 	int		ret = DDI_INTR_UNCLAIMED;
1676d4bc0535SKrishna Elango 	int		isrc;
1677d4bc0535SKrishna Elango 
1678d4bc0535SKrishna Elango 	if (!(pcieb_p->pcieb_init_flags & PCIEB_INIT_ENABLE))
1679d4bc0535SKrishna Elango 		goto FAIL;
1680d4bc0535SKrishna Elango 
1681d4bc0535SKrishna Elango 	mutex_enter(&pcieb_p->pcieb_intr_mutex);
1682d4bc0535SKrishna Elango 	isrc = pcieb_p->pcieb_isr_tab[(int)(uintptr_t)arg2];
1683d4bc0535SKrishna Elango 	mutex_exit(&pcieb_p->pcieb_intr_mutex);
1684d4bc0535SKrishna Elango 
1685d4bc0535SKrishna Elango 	PCIEB_DEBUG(DBG_INTR, dip, "Received intr number %d\n",
1686d4bc0535SKrishna Elango 	    (int)(uintptr_t)arg2);
1687d4bc0535SKrishna Elango 
1688d4bc0535SKrishna Elango 	if (isrc == PCIEB_INTR_SRC_UNKNOWN)
1689d4bc0535SKrishna Elango 		goto FAIL;
1690d4bc0535SKrishna Elango 
1691b3d69c05SRobert Mustacchi 	if (isrc & (PCIEB_INTR_SRC_HP | PCIEB_INTR_SRC_LBW))
169226947304SEvan Yan 		ret = pcie_intr(dip);
1693d4bc0535SKrishna Elango 
1694d4bc0535SKrishna Elango 	if (isrc & PCIEB_INTR_SRC_PME)
1695d4bc0535SKrishna Elango 		ret = DDI_INTR_CLAIMED;
1696d4bc0535SKrishna Elango 
1697d4bc0535SKrishna Elango 	/* AER Error */
1698d4bc0535SKrishna Elango 	if (isrc & PCIEB_INTR_SRC_AER) {
1699d4bc0535SKrishna Elango 		/*
1700*ac91f7fcSKeith M Wesolowski 		 * AERs can interrupt on this vector, so if error reporting is
1701*ac91f7fcSKeith M Wesolowski 		 * possible we need to scan the fabric to determine whether to
1702*ac91f7fcSKeith M Wesolowski 		 * claim it.  This process will also generate ereports if the
1703*ac91f7fcSKeith M Wesolowski 		 * bridge has error data for us.  Checking for EREPORT_CAPABLE
1704*ac91f7fcSKeith M Wesolowski 		 * is perhaps a bit of a formality here but if it's not set we
1705*ac91f7fcSKeith M Wesolowski 		 * aren't going to be able to do much that's useful with the AER
1706*ac91f7fcSKeith M Wesolowski 		 * data; we initialise sts to 0 rather than PF_ERR_NO_ERROR so
1707*ac91f7fcSKeith M Wesolowski 		 * that we'll claim this interrupt in that case, since we aren't
1708*ac91f7fcSKeith M Wesolowski 		 * going to do the scan.  It may be more correct to check the
1709*ac91f7fcSKeith M Wesolowski 		 * root port status ourselves in that case, but we aren't
1710*ac91f7fcSKeith M Wesolowski 		 * terribly worried about the case where we don't have FMA
1711*ac91f7fcSKeith M Wesolowski 		 * capabilities.
1712d4bc0535SKrishna Elango 		 */
1713d4bc0535SKrishna Elango 		bzero(&derr, sizeof (ddi_fm_error_t));
1714d4bc0535SKrishna Elango 		derr.fme_version = DDI_FME_VERSION;
1715d4bc0535SKrishna Elango 		mutex_enter(&pcieb_p->pcieb_peek_poke_mutex);
1716d4bc0535SKrishna Elango 		mutex_enter(&pcieb_p->pcieb_err_mutex);
1717d4bc0535SKrishna Elango 
1718fc256490SJason Beloro 		pf_eh_enter(PCIE_DIP2BUS(dip));
1719fc256490SJason Beloro 		PCIE_ROOT_EH_SRC(PCIE_DIP2PFD(dip))->intr_type =
1720fc256490SJason Beloro 		    PF_INTR_TYPE_AER;
1721fc256490SJason Beloro 
1722d4bc0535SKrishna Elango 		if ((DEVI(dip)->devi_fmhdl->fh_cap) & DDI_FM_EREPORT_CAPABLE)
1723d4bc0535SKrishna Elango 			sts = pf_scan_fabric(dip, &derr, NULL);
1724fc256490SJason Beloro 		pf_eh_exit(PCIE_DIP2BUS(dip));
1725d4bc0535SKrishna Elango 
1726d4bc0535SKrishna Elango 		mutex_exit(&pcieb_p->pcieb_err_mutex);
1727d4bc0535SKrishna Elango 		mutex_exit(&pcieb_p->pcieb_peek_poke_mutex);
1728*ac91f7fcSKeith M Wesolowski 		if ((pcieb_die & sts) != 0) {
1729d4bc0535SKrishna Elango 			fm_panic("%s-%d: PCI(-X) Express Fatal Error. (0x%x)",
1730d4bc0535SKrishna Elango 			    ddi_driver_name(dip), ddi_get_instance(dip), sts);
1731*ac91f7fcSKeith M Wesolowski 		}
1732*ac91f7fcSKeith M Wesolowski 
1733*ac91f7fcSKeith M Wesolowski 		if ((sts & ~PF_ERR_NO_ERROR) != 0)
1734*ac91f7fcSKeith M Wesolowski 			ret = DDI_INTR_CLAIMED;
1735d4bc0535SKrishna Elango 	}
1736d4bc0535SKrishna Elango FAIL:
1737d4bc0535SKrishna Elango 	return (ret);
1738d4bc0535SKrishna Elango }
1739d4bc0535SKrishna Elango 
1740d4bc0535SKrishna Elango /*
1741d4bc0535SKrishna Elango  * Some PCI-X to PCI-E bridges do not support full 64-bit addressing on the
1742d4bc0535SKrishna Elango  * PCI-X side of the bridge.  We build a special version of this driver for
1743d4bc0535SKrishna Elango  * those bridges, which uses PCIEB_ADDR_LIMIT_LO and/or PCIEB_ADDR_LIMIT_HI
1744d4bc0535SKrishna Elango  * to define the range of values which the chip can handle.  The code below
1745d4bc0535SKrishna Elango  * then clamps the DMA address range supplied by the driver, preventing the
1746d4bc0535SKrishna Elango  * PCI-E nexus driver from allocating any memory the bridge can't deal
1747d4bc0535SKrishna Elango  * with.
1748d4bc0535SKrishna Elango  */
1749d4bc0535SKrishna Elango static int
pcieb_dma_allochdl(dev_info_t * dip,dev_info_t * rdip,ddi_dma_attr_t * attr_p,int (* waitfp)(caddr_t),caddr_t arg,ddi_dma_handle_t * handlep)1750d4bc0535SKrishna Elango pcieb_dma_allochdl(dev_info_t *dip, dev_info_t *rdip,
175121922c75SToomas Soome     ddi_dma_attr_t *attr_p, int (*waitfp)(caddr_t), caddr_t arg,
175221922c75SToomas Soome     ddi_dma_handle_t *handlep)
1753d4bc0535SKrishna Elango {
1754d4bc0535SKrishna Elango 	int		ret;
1755af334d37SColin Zou - Sun Microsystems - Beijing China #ifdef	PCIEB_BCM
1756d4bc0535SKrishna Elango 	uint64_t	lim;
1757d4bc0535SKrishna Elango 
1758d4bc0535SKrishna Elango 	/*
1759d4bc0535SKrishna Elango 	 * If the leaf device's limits are outside than what the Broadcom
1760d4bc0535SKrishna Elango 	 * bridge can handle, we need to clip the values passed up the chain.
1761d4bc0535SKrishna Elango 	 */
1762d4bc0535SKrishna Elango 	lim = attr_p->dma_attr_addr_lo;
1763d4bc0535SKrishna Elango 	attr_p->dma_attr_addr_lo = MAX(lim, PCIEB_ADDR_LIMIT_LO);
1764d4bc0535SKrishna Elango 
1765d4bc0535SKrishna Elango 	lim = attr_p->dma_attr_addr_hi;
1766d4bc0535SKrishna Elango 	attr_p->dma_attr_addr_hi = MIN(lim, PCIEB_ADDR_LIMIT_HI);
1767d4bc0535SKrishna Elango 
1768af334d37SColin Zou - Sun Microsystems - Beijing China #endif	/* PCIEB_BCM */
1769d4bc0535SKrishna Elango 
1770d4bc0535SKrishna Elango 	/*
1771d4bc0535SKrishna Elango 	 * This is a software workaround to fix the Broadcom 5714/5715 PCIe-PCI
1772d4bc0535SKrishna Elango 	 * bridge prefetch bug. Intercept the DMA alloc handle request and set
1773d4bc0535SKrishna Elango 	 * PX_DMAI_FLAGS_MAP_BUFZONE flag in the handle. If this flag is set,
1774d4bc0535SKrishna Elango 	 * the px nexus driver will allocate an extra page & make it valid one,
1775d4bc0535SKrishna Elango 	 * for any DVMA request that comes from any of the Broadcom bridge child
1776d4bc0535SKrishna Elango 	 * devices.
1777d4bc0535SKrishna Elango 	 */
1778d4bc0535SKrishna Elango 	if ((ret = ddi_dma_allochdl(dip, rdip, attr_p, waitfp, arg,
1779d4bc0535SKrishna Elango 	    handlep)) == DDI_SUCCESS) {
1780d4bc0535SKrishna Elango 		ddi_dma_impl_t	*mp = (ddi_dma_impl_t *)*handlep;
1781af334d37SColin Zou - Sun Microsystems - Beijing China #ifdef	PCIEB_BCM
1782d4bc0535SKrishna Elango 		mp->dmai_inuse |= PX_DMAI_FLAGS_MAP_BUFZONE;
1783af334d37SColin Zou - Sun Microsystems - Beijing China #endif	/* PCIEB_BCM */
1784d4bc0535SKrishna Elango 		/*
1785d4bc0535SKrishna Elango 		 * For a given rdip, update mp->dmai_bdf with the bdf value
1786d4bc0535SKrishna Elango 		 * of pcieb's immediate child or secondary bus-id of the
1787d4bc0535SKrishna Elango 		 * PCIe2PCI bridge.
1788d4bc0535SKrishna Elango 		 */
1789d4bc0535SKrishna Elango 		mp->dmai_minxfer = pcie_get_bdf_for_dma_xfer(dip, rdip);
1790d4bc0535SKrishna Elango 	}
1791d4bc0535SKrishna Elango 
1792d4bc0535SKrishna Elango 	return (ret);
1793d4bc0535SKrishna Elango }
1794d4bc0535SKrishna Elango 
1795d4bc0535SKrishna Elango /*
1796d4bc0535SKrishna Elango  * FDVMA feature is not supported for any child device of Broadcom 5714/5715
1797d4bc0535SKrishna Elango  * PCIe-PCI bridge due to prefetch bug. Return failure immediately, so that
1798d4bc0535SKrishna Elango  * these drivers will switch to regular DVMA path.
1799d4bc0535SKrishna Elango  */
1800d4bc0535SKrishna Elango /*ARGSUSED*/
1801d4bc0535SKrishna Elango static int
pcieb_dma_mctl(dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t handle,enum ddi_dma_ctlops cmd,off_t * offp,size_t * lenp,caddr_t * objp,uint_t cache_flags)1802d4bc0535SKrishna Elango pcieb_dma_mctl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle,
180321922c75SToomas Soome     enum ddi_dma_ctlops cmd, off_t *offp, size_t *lenp, caddr_t *objp,
180421922c75SToomas Soome     uint_t cache_flags)
1805d4bc0535SKrishna Elango {
1806d4bc0535SKrishna Elango 	int	ret;
1807d4bc0535SKrishna Elango 
1808af334d37SColin Zou - Sun Microsystems - Beijing China #ifdef	PCIEB_BCM
1809d4bc0535SKrishna Elango 	if (cmd == DDI_DMA_RESERVE)
1810d4bc0535SKrishna Elango 		return (DDI_FAILURE);
1811af334d37SColin Zou - Sun Microsystems - Beijing China #endif	/* PCIEB_BCM */
1812d4bc0535SKrishna Elango 
1813d4bc0535SKrishna Elango 	if (((ret = ddi_dma_mctl(dip, rdip, handle, cmd, offp, lenp, objp,
1814d4bc0535SKrishna Elango 	    cache_flags)) == DDI_SUCCESS) && (cmd == DDI_DMA_RESERVE)) {
1815d4bc0535SKrishna Elango 		ddi_dma_impl_t	*mp = (ddi_dma_impl_t *)*objp;
1816d4bc0535SKrishna Elango 
1817d4bc0535SKrishna Elango 		/*
1818d4bc0535SKrishna Elango 		 * For a given rdip, update mp->dmai_bdf with the bdf value
1819d4bc0535SKrishna Elango 		 * of pcieb's immediate child or secondary bus-id of the
1820d4bc0535SKrishna Elango 		 * PCIe2PCI bridge.
1821d4bc0535SKrishna Elango 		 */
1822d4bc0535SKrishna Elango 		mp->dmai_minxfer = pcie_get_bdf_for_dma_xfer(dip, rdip);
1823d4bc0535SKrishna Elango 	}
1824d4bc0535SKrishna Elango 
1825d4bc0535SKrishna Elango 	return (ret);
1826d4bc0535SKrishna Elango }
1827d4bc0535SKrishna Elango 
1828d4bc0535SKrishna Elango static int
pcieb_intr_ops(dev_info_t * dip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)1829d4bc0535SKrishna Elango pcieb_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
1830d4bc0535SKrishna Elango     ddi_intr_handle_impl_t *hdlp, void *result)
1831d4bc0535SKrishna Elango {
1832d4bc0535SKrishna Elango 	return (pcieb_plat_intr_ops(dip, rdip, intr_op, hdlp, result));
1833d4bc0535SKrishna Elango 
1834d4bc0535SKrishna Elango }
1835d4bc0535SKrishna Elango 
1836d4bc0535SKrishna Elango /*
1837d4bc0535SKrishna Elango  * Power management related initialization specific to pcieb.
1838d4bc0535SKrishna Elango  * Called by pcieb_attach()
1839d4bc0535SKrishna Elango  */
1840d4bc0535SKrishna Elango static int
pcieb_pwr_setup(dev_info_t * dip)1841d4bc0535SKrishna Elango pcieb_pwr_setup(dev_info_t *dip)
1842d4bc0535SKrishna Elango {
1843d4bc0535SKrishna Elango 	char *comp_array[5];
1844d4bc0535SKrishna Elango 	int i;
1845d4bc0535SKrishna Elango 	ddi_acc_handle_t conf_hdl;
1846d4bc0535SKrishna Elango 	uint16_t pmcap, cap_ptr;
1847d4bc0535SKrishna Elango 	pcie_pwr_t *pwr_p;
1848d4bc0535SKrishna Elango 
1849d4bc0535SKrishna Elango 	/* Some platforms/devices may choose to disable PM */
1850d4bc0535SKrishna Elango 	if (pcieb_plat_pwr_disable(dip)) {
1851d4bc0535SKrishna Elango 		(void) pcieb_pwr_disable(dip);
1852d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
1853d4bc0535SKrishna Elango 	}
1854d4bc0535SKrishna Elango 
1855d4bc0535SKrishna Elango 	ASSERT(PCIE_PMINFO(dip));
1856d4bc0535SKrishna Elango 	pwr_p = PCIE_NEXUS_PMINFO(dip);
1857d4bc0535SKrishna Elango 	ASSERT(pwr_p);
1858d4bc0535SKrishna Elango 
1859d4bc0535SKrishna Elango 	/* Code taken from pci_pci driver */
1860d4bc0535SKrishna Elango 	if (pci_config_setup(dip, &pwr_p->pwr_conf_hdl) != DDI_SUCCESS) {
1861d4bc0535SKrishna Elango 		PCIEB_DEBUG(DBG_PWR, dip, "pcieb_pwr_setup: pci_config_setup "
1862d4bc0535SKrishna Elango 		    "failed\n");
1863d4bc0535SKrishna Elango 		return (DDI_FAILURE);
1864d4bc0535SKrishna Elango 	}
1865d4bc0535SKrishna Elango 	conf_hdl = pwr_p->pwr_conf_hdl;
1866d4bc0535SKrishna Elango 
1867d4bc0535SKrishna Elango 	/*
1868d4bc0535SKrishna Elango 	 * Walk the capabilities searching for a PM entry.
1869d4bc0535SKrishna Elango 	 */
1870d4bc0535SKrishna Elango 	if ((PCI_CAP_LOCATE(conf_hdl, PCI_CAP_ID_PM, &cap_ptr)) ==
1871d4bc0535SKrishna Elango 	    DDI_FAILURE) {
1872d4bc0535SKrishna Elango 		PCIEB_DEBUG(DBG_PWR, dip, "switch/bridge does not support PM. "
1873d4bc0535SKrishna Elango 		    " PCI PM data structure not found in config header\n");
1874d4bc0535SKrishna Elango 		pci_config_teardown(&conf_hdl);
1875d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
1876d4bc0535SKrishna Elango 	}
1877d4bc0535SKrishna Elango 	/*
1878d4bc0535SKrishna Elango 	 * Save offset to pmcsr for future references.
1879d4bc0535SKrishna Elango 	 */
1880d4bc0535SKrishna Elango 	pwr_p->pwr_pmcsr_offset = cap_ptr + PCI_PMCSR;
188121922c75SToomas Soome 	pmcap = PCI_CAP_GET16(conf_hdl, 0, cap_ptr, PCI_PMCAP);
1882d4bc0535SKrishna Elango 	if (pmcap & PCI_PMCAP_D1) {
1883d4bc0535SKrishna Elango 		PCIEB_DEBUG(DBG_PWR, dip, "D1 state supported\n");
1884d4bc0535SKrishna Elango 		pwr_p->pwr_pmcaps |= PCIE_SUPPORTS_D1;
1885d4bc0535SKrishna Elango 	}
1886d4bc0535SKrishna Elango 	if (pmcap & PCI_PMCAP_D2) {
1887d4bc0535SKrishna Elango 		PCIEB_DEBUG(DBG_PWR, dip, "D2 state supported\n");
1888d4bc0535SKrishna Elango 		pwr_p->pwr_pmcaps |= PCIE_SUPPORTS_D2;
1889d4bc0535SKrishna Elango 	}
1890d4bc0535SKrishna Elango 
1891d4bc0535SKrishna Elango 	i = 0;
1892d4bc0535SKrishna Elango 	comp_array[i++] = "NAME=PCIe switch/bridge PM";
1893d4bc0535SKrishna Elango 	comp_array[i++] = "0=Power Off (D3)";
1894d4bc0535SKrishna Elango 	if (pwr_p->pwr_pmcaps & PCIE_SUPPORTS_D2)
1895d4bc0535SKrishna Elango 		comp_array[i++] = "1=D2";
1896d4bc0535SKrishna Elango 	if (pwr_p->pwr_pmcaps & PCIE_SUPPORTS_D1)
1897d4bc0535SKrishna Elango 		comp_array[i++] = "2=D1";
1898d4bc0535SKrishna Elango 	comp_array[i++] = "3=Full Power D0";
1899d4bc0535SKrishna Elango 
1900d4bc0535SKrishna Elango 	/*
1901d4bc0535SKrishna Elango 	 * Create pm-components property, if it does not exist already.
1902d4bc0535SKrishna Elango 	 */
1903d4bc0535SKrishna Elango 	if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
1904d4bc0535SKrishna Elango 	    "pm-components", comp_array, i) != DDI_PROP_SUCCESS) {
1905d4bc0535SKrishna Elango 		PCIEB_DEBUG(DBG_PWR, dip, "could not create pm-components "
1906d4bc0535SKrishna Elango 		    " prop\n");
1907d4bc0535SKrishna Elango 		pci_config_teardown(&conf_hdl);
1908d4bc0535SKrishna Elango 		return (DDI_FAILURE);
1909d4bc0535SKrishna Elango 	}
1910d4bc0535SKrishna Elango 	return (pcieb_pwr_init_and_raise(dip, pwr_p));
1911d4bc0535SKrishna Elango }
1912d4bc0535SKrishna Elango 
1913d4bc0535SKrishna Elango /*
1914d4bc0535SKrishna Elango  * undo whatever is done in pcieb_pwr_setup. called by pcieb_detach()
1915d4bc0535SKrishna Elango  */
1916d4bc0535SKrishna Elango static void
pcieb_pwr_teardown(dev_info_t * dip)1917d4bc0535SKrishna Elango pcieb_pwr_teardown(dev_info_t *dip)
1918d4bc0535SKrishna Elango {
1919d4bc0535SKrishna Elango 	pcie_pwr_t	*pwr_p;
1920d4bc0535SKrishna Elango 
1921d4bc0535SKrishna Elango 	if (!PCIE_PMINFO(dip) || !(pwr_p = PCIE_NEXUS_PMINFO(dip)))
1922d4bc0535SKrishna Elango 		return;
1923d4bc0535SKrishna Elango 
1924d4bc0535SKrishna Elango 	(void) ddi_prop_remove(DDI_DEV_T_NONE, dip, "pm-components");
1925d4bc0535SKrishna Elango 	if (pwr_p->pwr_conf_hdl)
1926d4bc0535SKrishna Elango 		pci_config_teardown(&pwr_p->pwr_conf_hdl);
1927d4bc0535SKrishna Elango }
1928d4bc0535SKrishna Elango 
1929d4bc0535SKrishna Elango /*
1930d4bc0535SKrishna Elango  * Initializes the power level and raise the power to D0, if it is
1931d4bc0535SKrishna Elango  * not at D0.
1932d4bc0535SKrishna Elango  */
1933d4bc0535SKrishna Elango static int
pcieb_pwr_init_and_raise(dev_info_t * dip,pcie_pwr_t * pwr_p)1934d4bc0535SKrishna Elango pcieb_pwr_init_and_raise(dev_info_t *dip, pcie_pwr_t *pwr_p)
1935d4bc0535SKrishna Elango {
1936d4bc0535SKrishna Elango 	uint16_t pmcsr;
1937d4bc0535SKrishna Elango 	int ret = DDI_SUCCESS;
1938d4bc0535SKrishna Elango 
1939d4bc0535SKrishna Elango 	/*
1940d4bc0535SKrishna Elango 	 * Intialize our power level from PMCSR. The common code initializes
1941d4bc0535SKrishna Elango 	 * this to UNKNOWN. There is no guarantee that we will be at full
1942d4bc0535SKrishna Elango 	 * power at attach. If we are not at D0, raise the power.
1943d4bc0535SKrishna Elango 	 */
1944d4bc0535SKrishna Elango 	pmcsr = pci_config_get16(pwr_p->pwr_conf_hdl, pwr_p->pwr_pmcsr_offset);
1945d4bc0535SKrishna Elango 	pmcsr &= PCI_PMCSR_STATE_MASK;
1946d4bc0535SKrishna Elango 	switch (pmcsr) {
1947d4bc0535SKrishna Elango 	case PCI_PMCSR_D0:
1948d4bc0535SKrishna Elango 		pwr_p->pwr_func_lvl = PM_LEVEL_D0;
1949d4bc0535SKrishna Elango 		break;
1950d4bc0535SKrishna Elango 
1951d4bc0535SKrishna Elango 	case PCI_PMCSR_D1:
1952d4bc0535SKrishna Elango 		pwr_p->pwr_func_lvl = PM_LEVEL_D1;
1953d4bc0535SKrishna Elango 		break;
1954d4bc0535SKrishna Elango 
1955d4bc0535SKrishna Elango 	case PCI_PMCSR_D2:
1956d4bc0535SKrishna Elango 		pwr_p->pwr_func_lvl = PM_LEVEL_D2;
1957d4bc0535SKrishna Elango 		break;
1958d4bc0535SKrishna Elango 
1959d4bc0535SKrishna Elango 	case PCI_PMCSR_D3HOT:
1960d4bc0535SKrishna Elango 		pwr_p->pwr_func_lvl = PM_LEVEL_D3;
1961d4bc0535SKrishna Elango 		break;
1962d4bc0535SKrishna Elango 
1963d4bc0535SKrishna Elango 	default:
1964d4bc0535SKrishna Elango 		break;
1965d4bc0535SKrishna Elango 	}
1966d4bc0535SKrishna Elango 
1967d4bc0535SKrishna Elango 	/* Raise the power to D0. */
1968d4bc0535SKrishna Elango 	if (pwr_p->pwr_func_lvl != PM_LEVEL_D0 &&
1969d4bc0535SKrishna Elango 	    ((ret = pm_raise_power(dip, 0, PM_LEVEL_D0)) != DDI_SUCCESS)) {
1970d4bc0535SKrishna Elango 		/*
1971d4bc0535SKrishna Elango 		 * Read PMCSR again. If it is at D0, ignore the return
1972d4bc0535SKrishna Elango 		 * value from pm_raise_power.
1973d4bc0535SKrishna Elango 		 */
1974d4bc0535SKrishna Elango 		pmcsr = pci_config_get16(pwr_p->pwr_conf_hdl,
1975d4bc0535SKrishna Elango 		    pwr_p->pwr_pmcsr_offset);
1976d4bc0535SKrishna Elango 		if ((pmcsr & PCI_PMCSR_STATE_MASK) == PCI_PMCSR_D0)
1977d4bc0535SKrishna Elango 			ret = DDI_SUCCESS;
1978d4bc0535SKrishna Elango 		else {
1979d4bc0535SKrishna Elango 			PCIEB_DEBUG(DBG_PWR, dip, "pcieb_pwr_setup: could not "
1980d4bc0535SKrishna Elango 			    "raise power to D0 \n");
1981d4bc0535SKrishna Elango 		}
1982d4bc0535SKrishna Elango 	}
1983d4bc0535SKrishna Elango 	if (ret == DDI_SUCCESS)
1984d4bc0535SKrishna Elango 		pwr_p->pwr_func_lvl = PM_LEVEL_D0;
1985d4bc0535SKrishna Elango 	return (ret);
1986d4bc0535SKrishna Elango }
1987d4bc0535SKrishna Elango 
1988d4bc0535SKrishna Elango /*
1989d4bc0535SKrishna Elango  * Disable PM for x86 and PLX 8532 switch.
1990d4bc0535SKrishna Elango  * For PLX Transitioning one port on this switch to low power causes links
1991d4bc0535SKrishna Elango  * on other ports on the same station to die. Due to PLX erratum #34, we
1992d4bc0535SKrishna Elango  * can't allow the downstream device go to non-D0 state.
1993d4bc0535SKrishna Elango  */
1994d4bc0535SKrishna Elango static int
pcieb_pwr_disable(dev_info_t * dip)1995d4bc0535SKrishna Elango pcieb_pwr_disable(dev_info_t *dip)
1996d4bc0535SKrishna Elango {
1997d4bc0535SKrishna Elango 	pcie_pwr_t *pwr_p;
1998d4bc0535SKrishna Elango 
1999d4bc0535SKrishna Elango 	ASSERT(PCIE_PMINFO(dip));
2000d4bc0535SKrishna Elango 	pwr_p = PCIE_NEXUS_PMINFO(dip);
2001d4bc0535SKrishna Elango 	ASSERT(pwr_p);
2002d4bc0535SKrishna Elango 	PCIEB_DEBUG(DBG_PWR, dip, "pcieb_pwr_disable: disabling PM\n");
2003d4bc0535SKrishna Elango 	pwr_p->pwr_func_lvl = PM_LEVEL_D0;
2004d4bc0535SKrishna Elango 	pwr_p->pwr_flags = PCIE_NO_CHILD_PM;
2005d4bc0535SKrishna Elango 	return (DDI_SUCCESS);
2006d4bc0535SKrishna Elango }
2007d4bc0535SKrishna Elango 
2008d4bc0535SKrishna Elango #ifdef DEBUG
2009d4bc0535SKrishna Elango int pcieb_dbg_intr_print = 0;
2010d4bc0535SKrishna Elango void
pcieb_dbg(uint_t bit,dev_info_t * dip,char * fmt,...)2011d4bc0535SKrishna Elango pcieb_dbg(uint_t bit, dev_info_t *dip, char *fmt, ...)
2012d4bc0535SKrishna Elango {
2013d4bc0535SKrishna Elango 	va_list ap;
2014d4bc0535SKrishna Elango 
2015d4bc0535SKrishna Elango 	if (!pcieb_dbg_print)
2016d4bc0535SKrishna Elango 		return;
2017d4bc0535SKrishna Elango 
2018d4bc0535SKrishna Elango 	if (dip)
2019d4bc0535SKrishna Elango 		prom_printf("%s(%d): %s", ddi_driver_name(dip),
2020d4bc0535SKrishna Elango 		    ddi_get_instance(dip), pcieb_debug_sym[bit]);
2021d4bc0535SKrishna Elango 
2022d4bc0535SKrishna Elango 	va_start(ap, fmt);
2023d4bc0535SKrishna Elango 	if (servicing_interrupt()) {
2024d4bc0535SKrishna Elango 		if (pcieb_dbg_intr_print)
2025d4bc0535SKrishna Elango 			prom_vprintf(fmt, ap);
2026d4bc0535SKrishna Elango 	} else {
2027d4bc0535SKrishna Elango 		prom_vprintf(fmt, ap);
2028d4bc0535SKrishna Elango 	}
2029d4bc0535SKrishna Elango 
2030d4bc0535SKrishna Elango 	va_end(ap);
2031d4bc0535SKrishna Elango }
2032d4bc0535SKrishna Elango #endif
2033d4bc0535SKrishna Elango 
2034d4bc0535SKrishna Elango static void
pcieb_id_props(pcieb_devstate_t * pcieb)2035d4bc0535SKrishna Elango pcieb_id_props(pcieb_devstate_t *pcieb)
2036d4bc0535SKrishna Elango {
2037d4bc0535SKrishna Elango 	uint64_t serialid = 0;	/* 40b field of EUI-64 serial no. register */
2038d4bc0535SKrishna Elango 	uint16_t cap_ptr;
2039d4bc0535SKrishna Elango 	uint8_t fic = 0;	/* 1 = first in chassis device */
2040d4bc0535SKrishna Elango 	pcie_bus_t *bus_p = PCIE_DIP2BUS(pcieb->pcieb_dip);
2041d4bc0535SKrishna Elango 	ddi_acc_handle_t config_handle = bus_p->bus_cfg_hdl;
2042d4bc0535SKrishna Elango 
2043d4bc0535SKrishna Elango 	/*
2044d4bc0535SKrishna Elango 	 * Identify first in chassis.  In the special case of a Sun branded
2045d4bc0535SKrishna Elango 	 * PLX device, it obviously is first in chassis.  Otherwise, in the
2046d4bc0535SKrishna Elango 	 * general case, look for an Expansion Slot Register and check its
2047d4bc0535SKrishna Elango 	 * first-in-chassis bit.
2048d4bc0535SKrishna Elango 	 */
2049d4bc0535SKrishna Elango #ifdef	PX_PLX
2050d4bc0535SKrishna Elango 	uint16_t vendor_id = bus_p->bus_dev_ven_id & 0xFFFF;
2051d4bc0535SKrishna Elango 	uint16_t device_id = bus_p->bus_dev_ven_id >> 16;
2052d4bc0535SKrishna Elango 	if ((vendor_id == PXB_VENDOR_SUN) &&
2053d4bc0535SKrishna Elango 	    ((device_id == PXB_DEVICE_PLX_PCIX) ||
2054d4bc0535SKrishna Elango 	    (device_id == PXB_DEVICE_PLX_PCIE))) {
2055d4bc0535SKrishna Elango 		fic = 1;
2056d4bc0535SKrishna Elango 	}
2057d4bc0535SKrishna Elango #endif	/* PX_PLX */
2058d4bc0535SKrishna Elango 	if ((fic == 0) && ((PCI_CAP_LOCATE(config_handle,
2059d4bc0535SKrishna Elango 	    PCI_CAP_ID_SLOT_ID, &cap_ptr)) != DDI_FAILURE)) {
206021922c75SToomas Soome 		uint8_t esr = PCI_CAP_GET8(config_handle, 0,
2061d4bc0535SKrishna Elango 		    cap_ptr, PCI_CAP_ID_REGS_OFF);
2062d4bc0535SKrishna Elango 		if (PCI_CAPSLOT_FIC(esr))
2063d4bc0535SKrishna Elango 			fic = 1;
2064d4bc0535SKrishna Elango 	}
2065d4bc0535SKrishna Elango 
2066d4bc0535SKrishna Elango 	if ((PCI_CAP_LOCATE(config_handle,
2067d4bc0535SKrishna Elango 	    PCI_CAP_XCFG_SPC(PCIE_EXT_CAP_ID_SER), &cap_ptr)) != DDI_FAILURE) {
2068d4bc0535SKrishna Elango 		/* Serialid can be 0 thru a full 40b number */
206921922c75SToomas Soome 		serialid = PCI_XCAP_GET32(config_handle, 0,
2070d4bc0535SKrishna Elango 		    cap_ptr, PCIE_SER_SID_UPPER_DW);
2071d4bc0535SKrishna Elango 		serialid <<= 32;
207221922c75SToomas Soome 		serialid |= PCI_XCAP_GET32(config_handle, 0,
2073d4bc0535SKrishna Elango 		    cap_ptr, PCIE_SER_SID_LOWER_DW);
2074d4bc0535SKrishna Elango 	}
2075d4bc0535SKrishna Elango 
2076d4bc0535SKrishna Elango 	if (fic)
2077d4bc0535SKrishna Elango 		(void) ndi_prop_create_boolean(DDI_DEV_T_NONE, pcieb->pcieb_dip,
2078d4bc0535SKrishna Elango 		    "first-in-chassis");
2079d4bc0535SKrishna Elango 	if (serialid)
2080d4bc0535SKrishna Elango 		(void) ddi_prop_update_int64(DDI_DEV_T_NONE, pcieb->pcieb_dip,
2081d4bc0535SKrishna Elango 		    "serialid#", serialid);
2082d4bc0535SKrishna Elango }
2083d4bc0535SKrishna Elango 
2084d4bc0535SKrishna Elango static void
pcieb_create_ranges_prop(dev_info_t * dip,ddi_acc_handle_t config_handle)2085d4bc0535SKrishna Elango pcieb_create_ranges_prop(dev_info_t *dip,
208621922c75SToomas Soome     ddi_acc_handle_t config_handle)
2087d4bc0535SKrishna Elango {
2088d4bc0535SKrishna Elango 	uint32_t base, limit;
208926947304SEvan Yan 	ppb_ranges_t	ranges[PCIEB_RANGE_LEN];
2090d4bc0535SKrishna Elango 	uint8_t io_base_lo, io_limit_lo;
2091d4bc0535SKrishna Elango 	uint16_t io_base_hi, io_limit_hi, mem_base, mem_limit;
209226947304SEvan Yan 	int i = 0, rangelen = sizeof (ppb_ranges_t)/sizeof (int);
2093d4bc0535SKrishna Elango 
2094d4bc0535SKrishna Elango 	io_base_lo = pci_config_get8(config_handle, PCI_BCNF_IO_BASE_LOW);
2095d4bc0535SKrishna Elango 	io_limit_lo = pci_config_get8(config_handle, PCI_BCNF_IO_LIMIT_LOW);
2096d4bc0535SKrishna Elango 	io_base_hi = pci_config_get16(config_handle, PCI_BCNF_IO_BASE_HI);
2097d4bc0535SKrishna Elango 	io_limit_hi = pci_config_get16(config_handle, PCI_BCNF_IO_LIMIT_HI);
2098d4bc0535SKrishna Elango 	mem_base = pci_config_get16(config_handle, PCI_BCNF_MEM_BASE);
2099d4bc0535SKrishna Elango 	mem_limit = pci_config_get16(config_handle, PCI_BCNF_MEM_LIMIT);
2100d4bc0535SKrishna Elango 
2101d4bc0535SKrishna Elango 	/*
2102d4bc0535SKrishna Elango 	 * Create ranges for IO space
2103d4bc0535SKrishna Elango 	 */
2104d4bc0535SKrishna Elango 	ranges[i].size_low = ranges[i].size_high = 0;
2105d4bc0535SKrishna Elango 	ranges[i].parent_mid = ranges[i].child_mid = ranges[i].parent_high = 0;
2106d4bc0535SKrishna Elango 	ranges[i].child_high = ranges[i].parent_high |=
2107d4bc0535SKrishna Elango 	    (PCI_REG_REL_M | PCI_ADDR_IO);
2108d4bc0535SKrishna Elango 	base = PCIEB_16bit_IOADDR(io_base_lo);
2109d4bc0535SKrishna Elango 	limit = PCIEB_16bit_IOADDR(io_limit_lo);
2110d4bc0535SKrishna Elango 
2111d4bc0535SKrishna Elango 	if ((io_base_lo & 0xf) == PCIEB_32BIT_IO) {
2112d4bc0535SKrishna Elango 		base = PCIEB_LADDR(base, io_base_hi);
2113d4bc0535SKrishna Elango 	}
2114d4bc0535SKrishna Elango 	if ((io_limit_lo & 0xf) == PCIEB_32BIT_IO) {
2115d4bc0535SKrishna Elango 		limit = PCIEB_LADDR(limit, io_limit_hi);
2116d4bc0535SKrishna Elango 	}
2117d4bc0535SKrishna Elango 
2118d4bc0535SKrishna Elango 	if ((io_base_lo & PCIEB_32BIT_IO) && (io_limit_hi > 0)) {
2119d4bc0535SKrishna Elango 		base = PCIEB_LADDR(base, io_base_hi);
2120d4bc0535SKrishna Elango 		limit = PCIEB_LADDR(limit, io_limit_hi);
2121d4bc0535SKrishna Elango 	}
2122d4bc0535SKrishna Elango 
2123d4bc0535SKrishna Elango 	/*
2124d4bc0535SKrishna Elango 	 * Create ranges for 32bit memory space
2125d4bc0535SKrishna Elango 	 */
2126d4bc0535SKrishna Elango 	base = PCIEB_32bit_MEMADDR(mem_base);
2127d4bc0535SKrishna Elango 	limit = PCIEB_32bit_MEMADDR(mem_limit);
2128d4bc0535SKrishna Elango 	ranges[i].size_low = ranges[i].size_high = 0;
2129d4bc0535SKrishna Elango 	ranges[i].parent_mid = ranges[i].child_mid = ranges[i].parent_high = 0;
2130d4bc0535SKrishna Elango 	ranges[i].child_high = ranges[i].parent_high |=
2131d4bc0535SKrishna Elango 	    (PCI_REG_REL_M | PCI_ADDR_MEM32);
2132d4bc0535SKrishna Elango 	ranges[i].child_low = ranges[i].parent_low = base;
2133d4bc0535SKrishna Elango 	if (limit >= base) {
2134d4bc0535SKrishna Elango 		ranges[i].size_low = limit - base + PCIEB_MEMGRAIN;
2135d4bc0535SKrishna Elango 		i++;
2136d4bc0535SKrishna Elango 	}
2137d4bc0535SKrishna Elango 
2138d4bc0535SKrishna Elango 	if (i) {
2139d4bc0535SKrishna Elango 		(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "ranges",
2140d4bc0535SKrishna Elango 		    (int *)ranges, i * rangelen);
2141d4bc0535SKrishna Elango 	}
2142d4bc0535SKrishna Elango }
2143d4bc0535SKrishna Elango 
2144d4bc0535SKrishna Elango /*
2145d4bc0535SKrishna Elango  * For PCI and PCI-X devices including PCIe2PCI bridge, initialize
2146d4bc0535SKrishna Elango  * cache-line-size and latency timer configuration registers.
2147d4bc0535SKrishna Elango  */
2148d4bc0535SKrishna Elango void
pcieb_set_pci_perf_parameters(dev_info_t * dip,ddi_acc_handle_t cfg_hdl)2149d4bc0535SKrishna Elango pcieb_set_pci_perf_parameters(dev_info_t *dip, ddi_acc_handle_t cfg_hdl)
2150d4bc0535SKrishna Elango {
2151d4bc0535SKrishna Elango 	uint_t	n;
2152d4bc0535SKrishna Elango 
2153d4bc0535SKrishna Elango 	/* Initialize cache-line-size configuration register if needed */
2154d4bc0535SKrishna Elango 	if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
2155d4bc0535SKrishna Elango 	    "cache-line-size", 0) == 0) {
2156d4bc0535SKrishna Elango 		pci_config_put8(cfg_hdl, PCI_CONF_CACHE_LINESZ,
2157d4bc0535SKrishna Elango 		    PCIEB_CACHE_LINE_SIZE);
2158d4bc0535SKrishna Elango 		n = pci_config_get8(cfg_hdl, PCI_CONF_CACHE_LINESZ);
2159d4bc0535SKrishna Elango 		if (n != 0) {
2160d4bc0535SKrishna Elango 			(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
2161d4bc0535SKrishna Elango 			    "cache-line-size", n);
2162d4bc0535SKrishna Elango 		}
2163d4bc0535SKrishna Elango 	}
2164d4bc0535SKrishna Elango 
2165d4bc0535SKrishna Elango 	/* Initialize latency timer configuration registers if needed */
2166d4bc0535SKrishna Elango 	if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
2167d4bc0535SKrishna Elango 	    "latency-timer", 0) == 0) {
2168d4bc0535SKrishna Elango 		uchar_t	min_gnt, latency_timer;
2169d4bc0535SKrishna Elango 		uchar_t header_type;
2170d4bc0535SKrishna Elango 
2171d4bc0535SKrishna Elango 		/* Determine the configuration header type */
2172d4bc0535SKrishna Elango 		header_type = pci_config_get8(cfg_hdl, PCI_CONF_HEADER);
2173d4bc0535SKrishna Elango 
2174d4bc0535SKrishna Elango 		if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) {
2175d4bc0535SKrishna Elango 			latency_timer = PCIEB_LATENCY_TIMER;
2176d4bc0535SKrishna Elango 			pci_config_put8(cfg_hdl, PCI_BCNF_LATENCY_TIMER,
2177d4bc0535SKrishna Elango 			    latency_timer);
2178d4bc0535SKrishna Elango 		} else {
2179d4bc0535SKrishna Elango 			min_gnt = pci_config_get8(cfg_hdl, PCI_CONF_MIN_G);
2180d4bc0535SKrishna Elango 			latency_timer = min_gnt * 8;
2181d4bc0535SKrishna Elango 		}
2182d4bc0535SKrishna Elango 
2183d4bc0535SKrishna Elango 		pci_config_put8(cfg_hdl, PCI_CONF_LATENCY_TIMER,
2184d4bc0535SKrishna Elango 		    latency_timer);
2185d4bc0535SKrishna Elango 		n = pci_config_get8(cfg_hdl, PCI_CONF_LATENCY_TIMER);
2186d4bc0535SKrishna Elango 		if (n != 0) {
2187d4bc0535SKrishna Elango 			(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
2188d4bc0535SKrishna Elango 			    "latency-timer", n);
2189d4bc0535SKrishna Elango 		}
2190d4bc0535SKrishna Elango 	}
2191d4bc0535SKrishna Elango }
2192