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