xref: /titanic_50/usr/src/uts/intel/io/pci/pci_pci.c (revision cd21e7c548ae2a3b5e522244bf798f2a6b4ba02d)
1ae115bc7Smrj /*
2ae115bc7Smrj  * CDDL HEADER START
3ae115bc7Smrj  *
4ae115bc7Smrj  * The contents of this file are subject to the terms of the
5ae115bc7Smrj  * Common Development and Distribution License (the "License").
6ae115bc7Smrj  * You may not use this file except in compliance with the License.
7ae115bc7Smrj  *
8ae115bc7Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9ae115bc7Smrj  * or http://www.opensolaris.org/os/licensing.
10ae115bc7Smrj  * See the License for the specific language governing permissions
11ae115bc7Smrj  * and limitations under the License.
12ae115bc7Smrj  *
13ae115bc7Smrj  * When distributing Covered Code, include this CDDL HEADER in each
14ae115bc7Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15ae115bc7Smrj  * If applicable, add the following below this CDDL HEADER, with the
16ae115bc7Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
17ae115bc7Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
18ae115bc7Smrj  *
19ae115bc7Smrj  * CDDL HEADER END
20ae115bc7Smrj  */
21ae115bc7Smrj /*
229757e35cSStephen Hanson  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23ae115bc7Smrj  * Use is subject to license terms.
24ae115bc7Smrj  */
25*cd21e7c5SGarrett D'Amore /*
26*cd21e7c5SGarrett D'Amore  * Copyright 2012 Garrett D'Amore <garrett@damore.org>.  All rights reserved.
27*cd21e7c5SGarrett D'Amore  */
28ae115bc7Smrj 
29ae115bc7Smrj /*
30ae115bc7Smrj  * PCI to PCI bus bridge nexus driver
31ae115bc7Smrj  */
32ae115bc7Smrj 
33ae115bc7Smrj #include <sys/conf.h>
34ae115bc7Smrj #include <sys/kmem.h>
35ae115bc7Smrj #include <sys/debug.h>
36ae115bc7Smrj #include <sys/modctl.h>
37ae115bc7Smrj #include <sys/autoconf.h>
38ae115bc7Smrj #include <sys/ddi_impldefs.h>
39ae115bc7Smrj #include <sys/pci.h>
4026947304SEvan Yan #include <sys/pci_impl.h>
41eae2e508Skrishnae #include <sys/pcie_impl.h>
42ae115bc7Smrj #include <sys/ddi.h>
43ae115bc7Smrj #include <sys/sunddi.h>
44ae115bc7Smrj #include <sys/sunndi.h>
45ae115bc7Smrj #include <sys/ddifm.h>
46ae115bc7Smrj #include <sys/ndifm.h>
47ae115bc7Smrj #include <sys/fm/protocol.h>
4826947304SEvan Yan #include <sys/hotplug/pci/pcie_hp.h>
49ae115bc7Smrj #include <sys/hotplug/pci/pcihp.h>
50ae115bc7Smrj #include <sys/pci_intr_lib.h>
51ae115bc7Smrj #include <sys/psm.h>
52cb7ea99dSJimmy Vetayases #include <sys/pci_cap.h>
53ae115bc7Smrj 
54ae115bc7Smrj /*
55ae115bc7Smrj  * The variable controls the default setting of the command register
56ae115bc7Smrj  * for pci devices.  See ppb_initchild() for details.
57ae115bc7Smrj  */
58ae115bc7Smrj static ushort_t ppb_command_default = PCI_COMM_ME | PCI_COMM_MAE | PCI_COMM_IO;
59ae115bc7Smrj 
60ae115bc7Smrj 
61ae115bc7Smrj static int	ppb_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *,
62ae115bc7Smrj 		    off_t, off_t, caddr_t *);
63ae115bc7Smrj static int	ppb_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
64ae115bc7Smrj 		    void *, void *);
65ae115bc7Smrj static int	ppb_fm_init(dev_info_t *, dev_info_t *, int,
66ae115bc7Smrj 		    ddi_iblock_cookie_t *);
67ae115bc7Smrj static int	ppb_fm_callback(dev_info_t *, ddi_fm_error_t *, const void *);
68ae115bc7Smrj static int	ppb_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t,
69ae115bc7Smrj 		    ddi_intr_handle_impl_t *, void *);
70ae115bc7Smrj 
71ae115bc7Smrj /*
72ae115bc7Smrj  * ppb_support_msi: Flag that controls MSI support across P2P Bridges.
73ae115bc7Smrj  * By default, MSI is not supported except for special cases like HT
74ae115bc7Smrj  * bridges/tunnels that have HT MSI mapping enabled.
75ae115bc7Smrj  *
76ae115bc7Smrj  * However, MSI support behavior can be patched on a system by changing
77ae115bc7Smrj  * the value of this flag as shown below:-
78ae115bc7Smrj  *	 0 = default value, MSI is allowed by this driver for special cases
79ae115bc7Smrj  *	 1 = MSI supported without any checks for this driver
80ae115bc7Smrj  *	-1 = MSI not supported at all
81ae115bc7Smrj  */
82ae115bc7Smrj int ppb_support_msi = 0;
83ae115bc7Smrj 
84ae115bc7Smrj /*
85ae115bc7Smrj  * Controls the usage of the Hypertransport MSI mapping capability
86ae115bc7Smrj  *	0 = default value, leave hardware function as it is
87ae115bc7Smrj  *	1 = always enable HT MSI mapping
88ae115bc7Smrj  *     -1 = always disable HT MSI mapping
89ae115bc7Smrj  */
90ae115bc7Smrj int ppb_support_ht_msimap = 0;
91ae115bc7Smrj 
92ae115bc7Smrj struct bus_ops ppb_bus_ops = {
93ae115bc7Smrj 	BUSO_REV,
94ae115bc7Smrj 	ppb_bus_map,
95ae115bc7Smrj 	0,
96ae115bc7Smrj 	0,
97ae115bc7Smrj 	0,
98ae115bc7Smrj 	i_ddi_map_fault,
99*cd21e7c5SGarrett D'Amore 	0,
100ae115bc7Smrj 	ddi_dma_allochdl,
101ae115bc7Smrj 	ddi_dma_freehdl,
102ae115bc7Smrj 	ddi_dma_bindhdl,
103ae115bc7Smrj 	ddi_dma_unbindhdl,
104ae115bc7Smrj 	ddi_dma_flush,
105ae115bc7Smrj 	ddi_dma_win,
106ae115bc7Smrj 	ddi_dma_mctl,
107ae115bc7Smrj 	ppb_ctlops,
108ae115bc7Smrj 	ddi_bus_prop_op,
109ae115bc7Smrj 	0,			/* (*bus_get_eventcookie)();	*/
110ae115bc7Smrj 	0,			/* (*bus_add_eventcall)();	*/
111ae115bc7Smrj 	0,			/* (*bus_remove_eventcall)();	*/
112ae115bc7Smrj 	0,			/* (*bus_post_event)();		*/
113ae115bc7Smrj 	0,			/* (*bus_intr_ctl)();		*/
114ae115bc7Smrj 	0,			/* (*bus_config)(); 		*/
115ae115bc7Smrj 	0,			/* (*bus_unconfig)(); 		*/
116ae115bc7Smrj 	ppb_fm_init,		/* (*bus_fm_init)(); 		*/
117ae115bc7Smrj 	NULL,			/* (*bus_fm_fini)(); 		*/
118ae115bc7Smrj 	NULL,			/* (*bus_fm_access_enter)(); 	*/
119ae115bc7Smrj 	NULL,			/* (*bus_fm_access_exit)(); 	*/
120ae115bc7Smrj 	NULL,			/* (*bus_power)(); 	*/
12126947304SEvan Yan 	ppb_intr_ops,		/* (*bus_intr_op)(); 		*/
12226947304SEvan Yan 	pcie_hp_common_ops	/* (*bus_hp_op)(); 		*/
123ae115bc7Smrj };
124ae115bc7Smrj 
125ae115bc7Smrj /*
126ae115bc7Smrj  * The goal here is to leverage off of the pcihp.c source without making
127ae115bc7Smrj  * changes to it.  Call into it's cb_ops directly if needed.
128ae115bc7Smrj  */
129ae115bc7Smrj static int	ppb_open(dev_t *, int, int, cred_t *);
130ae115bc7Smrj static int	ppb_close(dev_t, int, int, cred_t *);
131ae115bc7Smrj static int	ppb_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
132ae115bc7Smrj static int	ppb_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
133ae115bc7Smrj 		    caddr_t, int *);
134ae115bc7Smrj static int	ppb_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
135eae2e508Skrishnae static void	ppb_peekpoke_cb(dev_info_t *, ddi_fm_error_t *);
136ae115bc7Smrj 
137ae115bc7Smrj struct cb_ops ppb_cb_ops = {
138ae115bc7Smrj 	ppb_open,			/* open */
139ae115bc7Smrj 	ppb_close,			/* close */
140ae115bc7Smrj 	nodev,				/* strategy */
141ae115bc7Smrj 	nodev,				/* print */
142ae115bc7Smrj 	nodev,				/* dump */
143ae115bc7Smrj 	nodev,				/* read */
144ae115bc7Smrj 	nodev,				/* write */
145ae115bc7Smrj 	ppb_ioctl,			/* ioctl */
146ae115bc7Smrj 	nodev,				/* devmap */
147ae115bc7Smrj 	nodev,				/* mmap */
148ae115bc7Smrj 	nodev,				/* segmap */
149ae115bc7Smrj 	nochpoll,			/* poll */
150ae115bc7Smrj 	ppb_prop_op,			/* cb_prop_op */
151ae115bc7Smrj 	NULL,				/* streamtab */
152ae115bc7Smrj 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
153ae115bc7Smrj 	CB_REV,				/* rev */
154ae115bc7Smrj 	nodev,				/* int (*cb_aread)() */
155ae115bc7Smrj 	nodev				/* int (*cb_awrite)() */
156ae115bc7Smrj };
157ae115bc7Smrj 
158ae115bc7Smrj 
159ae115bc7Smrj static int ppb_probe(dev_info_t *);
160ae115bc7Smrj static int ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
161ae115bc7Smrj static int ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
162ae115bc7Smrj 
163ae115bc7Smrj struct dev_ops ppb_ops = {
164ae115bc7Smrj 	DEVO_REV,		/* devo_rev */
165ae115bc7Smrj 	0,			/* refcnt  */
166ae115bc7Smrj 	ppb_info,		/* info */
167ae115bc7Smrj 	nulldev,		/* identify */
168ae115bc7Smrj 	ppb_probe,		/* probe */
169ae115bc7Smrj 	ppb_attach,		/* attach */
170ae115bc7Smrj 	ppb_detach,		/* detach */
171ae115bc7Smrj 	nulldev,		/* reset */
172ae115bc7Smrj 	&ppb_cb_ops,		/* driver operations */
17319397407SSherry Moore 	&ppb_bus_ops,		/* bus operations */
17419397407SSherry Moore 	NULL,			/* power */
17519397407SSherry Moore 	ddi_quiesce_not_needed,		/* quiesce */
176ae115bc7Smrj };
177ae115bc7Smrj 
178ae115bc7Smrj /*
179ae115bc7Smrj  * Module linkage information for the kernel.
180ae115bc7Smrj  */
181ae115bc7Smrj 
182ae115bc7Smrj static struct modldrv modldrv = {
183ae115bc7Smrj 	&mod_driverops, /* Type of module */
18426947304SEvan Yan 	"Standard PCI to PCI bridge nexus driver",
185ae115bc7Smrj 	&ppb_ops,	/* driver ops */
186ae115bc7Smrj };
187ae115bc7Smrj 
188ae115bc7Smrj static struct modlinkage modlinkage = {
189ae115bc7Smrj 	MODREV_1,
190ae115bc7Smrj 	(void *)&modldrv,
191ae115bc7Smrj 	NULL
192ae115bc7Smrj };
193ae115bc7Smrj 
194ae115bc7Smrj /*
195ae115bc7Smrj  * soft state pointer and structure template:
196ae115bc7Smrj  */
197ae115bc7Smrj static void *ppb_state;
198ae115bc7Smrj 
199ae115bc7Smrj typedef struct {
200ae115bc7Smrj 	dev_info_t *dip;
201ae115bc7Smrj 	int ppb_fmcap;
202ae115bc7Smrj 	ddi_iblock_cookie_t ppb_fm_ibc;
20326947304SEvan Yan 	kmutex_t ppb_mutex;
204ae115bc7Smrj 	kmutex_t ppb_peek_poke_mutex;
205ae115bc7Smrj 	kmutex_t ppb_err_mutex;
206ae115bc7Smrj 
207ae115bc7Smrj 	/*
208ae115bc7Smrj 	 * cpr support:
209ae115bc7Smrj 	 */
210ae115bc7Smrj 	uint_t config_state_index;
211ae115bc7Smrj 	struct {
212ae115bc7Smrj 		dev_info_t *dip;
213ae115bc7Smrj 		ushort_t command;
214ae115bc7Smrj 		uchar_t cache_line_size;
215ae115bc7Smrj 		uchar_t latency_timer;
216ae115bc7Smrj 		uchar_t header_type;
217ae115bc7Smrj 		uchar_t sec_latency_timer;
218ae115bc7Smrj 		ushort_t bridge_control;
219ae115bc7Smrj 	} config_state[PCI_MAX_CHILDREN];
220eae2e508Skrishnae 
221c85864d8SKrishna Elango 	uint16_t parent_bus;
222ae115bc7Smrj } ppb_devstate_t;
223ae115bc7Smrj 
224ae115bc7Smrj 
225ae115bc7Smrj /*
226ae115bc7Smrj  * forward function declarations:
227ae115bc7Smrj  */
228ae115bc7Smrj static void	ppb_removechild(dev_info_t *);
229ae115bc7Smrj static int	ppb_initchild(dev_info_t *child);
230ae115bc7Smrj static void	ppb_save_config_regs(ppb_devstate_t *ppb_p);
231ae115bc7Smrj static void	ppb_restore_config_regs(ppb_devstate_t *ppb_p);
232ae115bc7Smrj static boolean_t	ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl);
233ae115bc7Smrj static int	ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd);
234ae115bc7Smrj 
235ae115bc7Smrj /*
236ae115bc7Smrj  * for <cmd> in ppb_ht_msimap_set
237ae115bc7Smrj  */
238ae115bc7Smrj #define	HT_MSIMAP_ENABLE	1
239ae115bc7Smrj #define	HT_MSIMAP_DISABLE	0
240ae115bc7Smrj 
241ae115bc7Smrj 
242ae115bc7Smrj int
_init(void)243ae115bc7Smrj _init(void)
244ae115bc7Smrj {
245ae115bc7Smrj 	int e;
246ae115bc7Smrj 
247ae115bc7Smrj 	if ((e = ddi_soft_state_init(&ppb_state, sizeof (ppb_devstate_t),
248ae115bc7Smrj 	    1)) == 0 && (e = mod_install(&modlinkage)) != 0)
249ae115bc7Smrj 		ddi_soft_state_fini(&ppb_state);
250ae115bc7Smrj 	return (e);
251ae115bc7Smrj }
252ae115bc7Smrj 
253ae115bc7Smrj int
_fini(void)254ae115bc7Smrj _fini(void)
255ae115bc7Smrj {
256ae115bc7Smrj 	int e;
257ae115bc7Smrj 
258ae115bc7Smrj 	if ((e = mod_remove(&modlinkage)) == 0)
259ae115bc7Smrj 		ddi_soft_state_fini(&ppb_state);
260ae115bc7Smrj 	return (e);
261ae115bc7Smrj }
262ae115bc7Smrj 
263ae115bc7Smrj int
_info(struct modinfo * modinfop)264ae115bc7Smrj _info(struct modinfo *modinfop)
265ae115bc7Smrj {
266ae115bc7Smrj 	return (mod_info(&modlinkage, modinfop));
267ae115bc7Smrj }
268ae115bc7Smrj 
269ae115bc7Smrj /*ARGSUSED*/
270ae115bc7Smrj static int
ppb_probe(dev_info_t * devi)271ae115bc7Smrj ppb_probe(dev_info_t *devi)
272ae115bc7Smrj {
273ae115bc7Smrj 	return (DDI_PROBE_SUCCESS);
274ae115bc7Smrj }
275ae115bc7Smrj 
276ae115bc7Smrj /*ARGSUSED*/
277ae115bc7Smrj static int
ppb_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)278ae115bc7Smrj ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
279ae115bc7Smrj {
280eae2e508Skrishnae 	dev_info_t *root = ddi_root_node();
281ae115bc7Smrj 	int instance;
282ae115bc7Smrj 	ppb_devstate_t *ppb;
283eae2e508Skrishnae 	dev_info_t *pdip;
284ae115bc7Smrj 	ddi_acc_handle_t config_handle;
285eae2e508Skrishnae 	char *bus;
28626947304SEvan Yan 	int ret;
287ae115bc7Smrj 
288ae115bc7Smrj 	switch (cmd) {
289ae115bc7Smrj 	case DDI_ATTACH:
290ae115bc7Smrj 
291ae115bc7Smrj 		/*
292ae115bc7Smrj 		 * Make sure the "device_type" property exists.
293ae115bc7Smrj 		 */
294ae115bc7Smrj 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
295ae115bc7Smrj 		    "device_type", "pci");
296ae115bc7Smrj 
297ae115bc7Smrj 		/*
298ae115bc7Smrj 		 * Allocate and get soft state structure.
299ae115bc7Smrj 		 */
300ae115bc7Smrj 		instance = ddi_get_instance(devi);
301ae115bc7Smrj 		if (ddi_soft_state_zalloc(ppb_state, instance) != DDI_SUCCESS)
302ae115bc7Smrj 			return (DDI_FAILURE);
303ae115bc7Smrj 		ppb = ddi_get_soft_state(ppb_state, instance);
304ae115bc7Smrj 		ppb->dip = devi;
305ae115bc7Smrj 
306ae115bc7Smrj 		/*
307ae115bc7Smrj 		 * don't enable ereports if immediate child of npe
308ae115bc7Smrj 		 */
309ae115bc7Smrj 		if (strcmp(ddi_driver_name(ddi_get_parent(devi)), "npe") == 0)
310ae115bc7Smrj 			ppb->ppb_fmcap = DDI_FM_ERRCB_CAPABLE |
311ae115bc7Smrj 			    DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
312ae115bc7Smrj 		else
313ae115bc7Smrj 			ppb->ppb_fmcap = DDI_FM_EREPORT_CAPABLE |
314ae115bc7Smrj 			    DDI_FM_ERRCB_CAPABLE | DDI_FM_ACCCHK_CAPABLE |
315ae115bc7Smrj 			    DDI_FM_DMACHK_CAPABLE;
316ae115bc7Smrj 
317ae115bc7Smrj 		ddi_fm_init(devi, &ppb->ppb_fmcap, &ppb->ppb_fm_ibc);
31826947304SEvan Yan 		mutex_init(&ppb->ppb_mutex, NULL, MUTEX_DRIVER, NULL);
319ae115bc7Smrj 		mutex_init(&ppb->ppb_err_mutex, NULL, MUTEX_DRIVER,
320ae115bc7Smrj 		    (void *)ppb->ppb_fm_ibc);
321ae115bc7Smrj 		mutex_init(&ppb->ppb_peek_poke_mutex, NULL, MUTEX_DRIVER,
322ae115bc7Smrj 		    (void *)ppb->ppb_fm_ibc);
323ae115bc7Smrj 
324ae115bc7Smrj 		if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
325ae115bc7Smrj 		    DDI_FM_EREPORT_CAPABLE))
326ae115bc7Smrj 			pci_ereport_setup(devi);
327ae115bc7Smrj 		if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
328ae115bc7Smrj 			ddi_fm_handler_register(devi, ppb_fm_callback, NULL);
329ae115bc7Smrj 
330ae115bc7Smrj 		if (pci_config_setup(devi, &config_handle) != DDI_SUCCESS) {
331ae115bc7Smrj 			if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
332ae115bc7Smrj 				ddi_fm_handler_unregister(devi);
333ae115bc7Smrj 			if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
334ae115bc7Smrj 			    DDI_FM_EREPORT_CAPABLE))
335ae115bc7Smrj 				pci_ereport_teardown(devi);
336ae115bc7Smrj 			ddi_fm_fini(devi);
337ae115bc7Smrj 			ddi_soft_state_free(ppb_state, instance);
338ae115bc7Smrj 			return (DDI_FAILURE);
339ae115bc7Smrj 		}
340ae115bc7Smrj 
341c85864d8SKrishna Elango 		ppb->parent_bus = PCIE_PCIECAP_DEV_TYPE_PCI_PSEUDO;
342eae2e508Skrishnae 		for (pdip = ddi_get_parent(devi); pdip && (pdip != root) &&
343eae2e508Skrishnae 		    (ppb->parent_bus != PCIE_PCIECAP_DEV_TYPE_PCIE_DEV);
344eae2e508Skrishnae 		    pdip = ddi_get_parent(pdip)) {
345eae2e508Skrishnae 			if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip,
346eae2e508Skrishnae 			    DDI_PROP_DONTPASS, "device_type", &bus) !=
347eae2e508Skrishnae 			    DDI_PROP_SUCCESS)
348eae2e508Skrishnae 				break;
349eae2e508Skrishnae 
350eae2e508Skrishnae 			if (strcmp(bus, "pciex") == 0)
351eae2e508Skrishnae 				ppb->parent_bus =
352eae2e508Skrishnae 				    PCIE_PCIECAP_DEV_TYPE_PCIE_DEV;
353eae2e508Skrishnae 
354eae2e508Skrishnae 			ddi_prop_free(bus);
355eae2e508Skrishnae 		}
356eae2e508Skrishnae 
357ae115bc7Smrj 		if (ppb_support_ht_msimap == 1)
358ae115bc7Smrj 			(void) ppb_ht_msimap_set(config_handle,
359ae115bc7Smrj 			    HT_MSIMAP_ENABLE);
360ae115bc7Smrj 		else if (ppb_support_ht_msimap == -1)
361ae115bc7Smrj 			(void) ppb_ht_msimap_set(config_handle,
362ae115bc7Smrj 			    HT_MSIMAP_DISABLE);
363ae115bc7Smrj 
364ae115bc7Smrj 		pci_config_teardown(&config_handle);
365ae115bc7Smrj 
366ae115bc7Smrj 		/*
36726947304SEvan Yan 		 * Initialize hotplug support on this bus.
368ae115bc7Smrj 		 */
36926947304SEvan Yan 		if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
37026947304SEvan Yan 			ret = pcie_init(devi, NULL);
37126947304SEvan Yan 		else
37226947304SEvan Yan 			ret = pcihp_init(devi);
37326947304SEvan Yan 
37426947304SEvan Yan 		if (ret != DDI_SUCCESS) {
3752df1fe9cSrandyf 			cmn_err(CE_WARN,
3762df1fe9cSrandyf 			    "pci: Failed to setup hotplug framework");
37726947304SEvan Yan 			(void) ppb_detach(devi, DDI_DETACH);
37826947304SEvan Yan 			return (ret);
37926947304SEvan Yan 		}
380ae115bc7Smrj 
381ae115bc7Smrj 		ddi_report_dev(devi);
382ae115bc7Smrj 		return (DDI_SUCCESS);
383ae115bc7Smrj 
384ae115bc7Smrj 	case DDI_RESUME:
385ae115bc7Smrj 
386ae115bc7Smrj 		/*
387ae115bc7Smrj 		 * Get the soft state structure for the bridge.
388ae115bc7Smrj 		 */
389ae115bc7Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
390ae115bc7Smrj 		ppb_restore_config_regs(ppb);
391ae115bc7Smrj 		return (DDI_SUCCESS);
392ae115bc7Smrj 
393ae115bc7Smrj 	default:
394ae115bc7Smrj 		break;
395ae115bc7Smrj 	}
396ae115bc7Smrj 	return (DDI_FAILURE);
397ae115bc7Smrj }
398ae115bc7Smrj 
399ae115bc7Smrj /*ARGSUSED*/
400ae115bc7Smrj static int
ppb_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)401ae115bc7Smrj ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
402ae115bc7Smrj {
403ae115bc7Smrj 	ppb_devstate_t *ppb;
40426947304SEvan Yan 	int		ret;
405ae115bc7Smrj 
406ae115bc7Smrj 	switch (cmd) {
407ae115bc7Smrj 	case DDI_DETACH:
408ae115bc7Smrj 		(void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type");
409ae115bc7Smrj 
410ae115bc7Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
411ae115bc7Smrj 		if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
412ae115bc7Smrj 			ddi_fm_handler_unregister(devi);
413ae115bc7Smrj 		if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
414ae115bc7Smrj 		    DDI_FM_EREPORT_CAPABLE))
415ae115bc7Smrj 			pci_ereport_teardown(devi);
41626947304SEvan Yan 
41726947304SEvan Yan 		/*
41826947304SEvan Yan 		 * Uninitialize hotplug support on this bus.
41926947304SEvan Yan 		 */
42026947304SEvan Yan 		ret = (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) ?
42126947304SEvan Yan 		    pcie_uninit(devi) : pcihp_uninit(devi);
42226947304SEvan Yan 		if (ret != DDI_SUCCESS)
42326947304SEvan Yan 			return (DDI_FAILURE);
42426947304SEvan Yan 
425ae115bc7Smrj 		mutex_destroy(&ppb->ppb_peek_poke_mutex);
426ae115bc7Smrj 		mutex_destroy(&ppb->ppb_err_mutex);
42726947304SEvan Yan 		mutex_destroy(&ppb->ppb_mutex);
428ae115bc7Smrj 		ddi_fm_fini(devi);
429ae115bc7Smrj 
430ae115bc7Smrj 		/*
431ae115bc7Smrj 		 * And finally free the per-pci soft state.
432ae115bc7Smrj 		 */
433ae115bc7Smrj 		ddi_soft_state_free(ppb_state, ddi_get_instance(devi));
434ae115bc7Smrj 
435ae115bc7Smrj 		return (DDI_SUCCESS);
436ae115bc7Smrj 
437ae115bc7Smrj 	case DDI_SUSPEND:
438ae115bc7Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
439ae115bc7Smrj 		ppb_save_config_regs(ppb);
440ae115bc7Smrj 		return (DDI_SUCCESS);
441ae115bc7Smrj 
442ae115bc7Smrj 	default:
443ae115bc7Smrj 		break;
444ae115bc7Smrj 	}
445ae115bc7Smrj 	return (DDI_FAILURE);
446ae115bc7Smrj }
447ae115bc7Smrj 
448ae115bc7Smrj /*ARGSUSED*/
449ae115bc7Smrj static int
ppb_bus_map(dev_info_t * dip,dev_info_t * rdip,ddi_map_req_t * mp,off_t offset,off_t len,caddr_t * vaddrp)450ae115bc7Smrj ppb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
451ae115bc7Smrj 	off_t offset, off_t len, caddr_t *vaddrp)
452ae115bc7Smrj {
453ae115bc7Smrj 	dev_info_t *pdip;
454837c1ac4SStephen Hanson 	ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state,
455837c1ac4SStephen Hanson 	    ddi_get_instance(dip));
456ae115bc7Smrj 
4579757e35cSStephen Hanson 	if (strcmp(ddi_driver_name(ddi_get_parent(dip)), "npe") == 0 &&
4589757e35cSStephen Hanson 	    mp->map_handlep != NULL) {
459837c1ac4SStephen Hanson 		ddi_acc_impl_t *hdlp =
460837c1ac4SStephen Hanson 		    (ddi_acc_impl_t *)(mp->map_handlep)->ah_platform_private;
461837c1ac4SStephen Hanson 		hdlp->ahi_err_mutexp = &ppb->ppb_err_mutex;
462837c1ac4SStephen Hanson 		hdlp->ahi_peekpoke_mutexp = &ppb->ppb_peek_poke_mutex;
463837c1ac4SStephen Hanson 		hdlp->ahi_scan_dip = dip;
464837c1ac4SStephen Hanson 		hdlp->ahi_scan = ppb_peekpoke_cb;
465837c1ac4SStephen Hanson 	}
466ae115bc7Smrj 	pdip = (dev_info_t *)DEVI(dip)->devi_parent;
467ae115bc7Smrj 	return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip,
468ae115bc7Smrj 	    rdip, mp, offset, len, vaddrp));
469ae115bc7Smrj }
470ae115bc7Smrj 
471ae115bc7Smrj /*ARGSUSED*/
472ae115bc7Smrj static int
ppb_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)473ae115bc7Smrj ppb_ctlops(dev_info_t *dip, dev_info_t *rdip,
474ae115bc7Smrj 	ddi_ctl_enum_t ctlop, void *arg, void *result)
475ae115bc7Smrj {
476ae115bc7Smrj 	pci_regspec_t *drv_regp;
477ae115bc7Smrj 	int	reglen;
478ae115bc7Smrj 	int	rn;
479ae115bc7Smrj 	int	totreg;
480eae2e508Skrishnae 	ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state,
481eae2e508Skrishnae 	    ddi_get_instance(dip));
482eae2e508Skrishnae 	struct detachspec *dsp;
4832df1fe9cSrandyf 	struct attachspec *asp;
484ae115bc7Smrj 
485ae115bc7Smrj 	switch (ctlop) {
486ae115bc7Smrj 	case DDI_CTLOPS_REPORTDEV:
487ae115bc7Smrj 		if (rdip == (dev_info_t *)0)
488ae115bc7Smrj 			return (DDI_FAILURE);
489ae115bc7Smrj 		cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n",
490ae115bc7Smrj 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
491ae115bc7Smrj 		    ddi_driver_name(rdip),
492ae115bc7Smrj 		    ddi_get_instance(rdip));
493ae115bc7Smrj 		return (DDI_SUCCESS);
494ae115bc7Smrj 
495ae115bc7Smrj 	case DDI_CTLOPS_INITCHILD:
496ae115bc7Smrj 		return (ppb_initchild((dev_info_t *)arg));
497ae115bc7Smrj 
498ae115bc7Smrj 	case DDI_CTLOPS_UNINITCHILD:
499ae115bc7Smrj 		ppb_removechild((dev_info_t *)arg);
500ae115bc7Smrj 		return (DDI_SUCCESS);
501ae115bc7Smrj 
502ae115bc7Smrj 	case DDI_CTLOPS_SIDDEV:
503ae115bc7Smrj 		return (DDI_SUCCESS);
504ae115bc7Smrj 
505ae115bc7Smrj 	case DDI_CTLOPS_REGSIZE:
506ae115bc7Smrj 	case DDI_CTLOPS_NREGS:
507ae115bc7Smrj 		if (rdip == (dev_info_t *)0)
508ae115bc7Smrj 			return (DDI_FAILURE);
509ae115bc7Smrj 		break;
510ae115bc7Smrj 
5112df1fe9cSrandyf 	/* X86 systems support PME wakeup from suspend */
5122df1fe9cSrandyf 	case DDI_CTLOPS_ATTACH:
513eae2e508Skrishnae 		if (!pcie_is_child(dip, rdip))
514eae2e508Skrishnae 			return (DDI_SUCCESS);
515eae2e508Skrishnae 
5162df1fe9cSrandyf 		asp = (struct attachspec *)arg;
517eae2e508Skrishnae 		if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) &&
518eae2e508Skrishnae 		    (asp->when == DDI_POST) && (asp->result == DDI_SUCCESS))
519eae2e508Skrishnae 			pf_init(rdip, (void *)ppb->ppb_fm_ibc, asp->cmd);
520eae2e508Skrishnae 
5212df1fe9cSrandyf 		if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE)
5222df1fe9cSrandyf 			if (pci_pre_resume(rdip) != DDI_SUCCESS)
5232df1fe9cSrandyf 				return (DDI_FAILURE);
5242df1fe9cSrandyf 
525eae2e508Skrishnae 		return (DDI_SUCCESS);
5262df1fe9cSrandyf 
5272df1fe9cSrandyf 	case DDI_CTLOPS_DETACH:
528eae2e508Skrishnae 		if (!pcie_is_child(dip, rdip))
529eae2e508Skrishnae 			return (DDI_SUCCESS);
530eae2e508Skrishnae 
531eae2e508Skrishnae 		dsp = (struct detachspec *)arg;
532eae2e508Skrishnae 		if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) &&
533eae2e508Skrishnae 		    (dsp->when == DDI_PRE))
534eae2e508Skrishnae 			pf_fini(rdip, dsp->cmd);
535eae2e508Skrishnae 
536eae2e508Skrishnae 		if (dsp->cmd == DDI_SUSPEND && dsp->when == DDI_POST)
5372df1fe9cSrandyf 			if (pci_post_suspend(rdip) != DDI_SUCCESS)
5382df1fe9cSrandyf 				return (DDI_FAILURE);
539eae2e508Skrishnae 
540eae2e508Skrishnae 		return (DDI_SUCCESS);
5412df1fe9cSrandyf 
542ae115bc7Smrj 	case DDI_CTLOPS_PEEK:
543ae115bc7Smrj 	case DDI_CTLOPS_POKE:
544ae115bc7Smrj 		if (strcmp(ddi_driver_name(ddi_get_parent(dip)), "npe") != 0)
545ae115bc7Smrj 			return (ddi_ctlops(dip, rdip, ctlop, arg, result));
546ae115bc7Smrj 		return (pci_peekpoke_check(dip, rdip, ctlop, arg, result,
547ae115bc7Smrj 		    ddi_ctlops, &ppb->ppb_err_mutex,
548eae2e508Skrishnae 		    &ppb->ppb_peek_poke_mutex, ppb_peekpoke_cb));
549ae115bc7Smrj 
550ae115bc7Smrj 	default:
551ae115bc7Smrj 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
552ae115bc7Smrj 	}
553ae115bc7Smrj 
554ae115bc7Smrj 	*(int *)result = 0;
555ae115bc7Smrj 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip,
556ae115bc7Smrj 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg",
557ae115bc7Smrj 	    (caddr_t)&drv_regp, &reglen) != DDI_SUCCESS)
558ae115bc7Smrj 		return (DDI_FAILURE);
559ae115bc7Smrj 
560ae115bc7Smrj 	totreg = reglen / sizeof (pci_regspec_t);
561ae115bc7Smrj 	if (ctlop == DDI_CTLOPS_NREGS)
562ae115bc7Smrj 		*(int *)result = totreg;
563ae115bc7Smrj 	else if (ctlop == DDI_CTLOPS_REGSIZE) {
564ae115bc7Smrj 		rn = *(int *)arg;
565ae115bc7Smrj 		if (rn >= totreg) {
566ae115bc7Smrj 			kmem_free(drv_regp, reglen);
567ae115bc7Smrj 			return (DDI_FAILURE);
568ae115bc7Smrj 		}
569ae115bc7Smrj 		*(off_t *)result = drv_regp[rn].pci_size_low;
570ae115bc7Smrj 	}
571ae115bc7Smrj 
572ae115bc7Smrj 	kmem_free(drv_regp, reglen);
573ae115bc7Smrj 	return (DDI_SUCCESS);
574ae115bc7Smrj }
575ae115bc7Smrj 
576ae115bc7Smrj static int
ppb_name_child(dev_info_t * child,char * name,int namelen)577ae115bc7Smrj ppb_name_child(dev_info_t *child, char *name, int namelen)
578ae115bc7Smrj {
579ae115bc7Smrj 	pci_regspec_t *pci_rp;
580ae115bc7Smrj 	uint_t slot, func;
581ae115bc7Smrj 	char **unit_addr;
582ae115bc7Smrj 	uint_t n;
583ae115bc7Smrj 
584ae115bc7Smrj 	/*
585ae115bc7Smrj 	 * For .conf nodes, use unit-address property as name
586ae115bc7Smrj 	 */
587ae115bc7Smrj 	if (ndi_dev_is_persistent_node(child) == 0) {
588ae115bc7Smrj 		if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
589ae115bc7Smrj 		    DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) !=
590ae115bc7Smrj 		    DDI_PROP_SUCCESS) {
591ae115bc7Smrj 			cmn_err(CE_WARN,
592ae115bc7Smrj 			    "cannot find unit-address in %s.conf",
593ae115bc7Smrj 			    ddi_driver_name(child));
594ae115bc7Smrj 			return (DDI_FAILURE);
595ae115bc7Smrj 		}
596ae115bc7Smrj 		if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
597ae115bc7Smrj 			cmn_err(CE_WARN, "unit-address property in %s.conf"
598ae115bc7Smrj 			    " not well-formed", ddi_driver_name(child));
599ae115bc7Smrj 			ddi_prop_free(unit_addr);
600ae115bc7Smrj 			return (DDI_SUCCESS);
601ae115bc7Smrj 		}
602ae115bc7Smrj 		(void) snprintf(name, namelen, "%s", *unit_addr);
603ae115bc7Smrj 		ddi_prop_free(unit_addr);
604ae115bc7Smrj 		return (DDI_SUCCESS);
605ae115bc7Smrj 	}
606ae115bc7Smrj 
607ae115bc7Smrj 	/* get child "reg" property */
608ae115bc7Smrj 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
609ae115bc7Smrj 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) {
610ae115bc7Smrj 		return (DDI_FAILURE);
611ae115bc7Smrj 	}
612ae115bc7Smrj 
613ae115bc7Smrj 	/* copy the device identifications */
614ae115bc7Smrj 	slot = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
615ae115bc7Smrj 	func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
616ae115bc7Smrj 
617ae115bc7Smrj 	if (func != 0)
618ae115bc7Smrj 		(void) snprintf(name, namelen, "%x,%x", slot, func);
619ae115bc7Smrj 	else
620ae115bc7Smrj 		(void) snprintf(name, namelen, "%x", slot);
621ae115bc7Smrj 
622ae115bc7Smrj 	ddi_prop_free(pci_rp);
623ae115bc7Smrj 	return (DDI_SUCCESS);
624ae115bc7Smrj }
625ae115bc7Smrj 
626ae115bc7Smrj static int
ppb_initchild(dev_info_t * child)627ae115bc7Smrj ppb_initchild(dev_info_t *child)
628ae115bc7Smrj {
629ae115bc7Smrj 	struct ddi_parent_private_data *pdptr;
630eae2e508Skrishnae 	ppb_devstate_t *ppb;
631ae115bc7Smrj 	char name[MAXNAMELEN];
632ae115bc7Smrj 	ddi_acc_handle_t config_handle;
633ae115bc7Smrj 	ushort_t command_preserve, command;
634ae115bc7Smrj 
635eae2e508Skrishnae 	ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
636eae2e508Skrishnae 	    ddi_get_instance(ddi_get_parent(child)));
637eae2e508Skrishnae 
638ae115bc7Smrj 	if (ppb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS)
639ae115bc7Smrj 		return (DDI_FAILURE);
640ae115bc7Smrj 	ddi_set_name_addr(child, name);
641ae115bc7Smrj 
642ae115bc7Smrj 	/*
643ae115bc7Smrj 	 * Pseudo nodes indicate a prototype node with per-instance
644ae115bc7Smrj 	 * properties to be merged into the real h/w device node.
645ae115bc7Smrj 	 * The interpretation of the unit-address is DD[,F]
646ae115bc7Smrj 	 * where DD is the device id and F is the function.
647ae115bc7Smrj 	 */
648ae115bc7Smrj 	if (ndi_dev_is_persistent_node(child) == 0) {
649ae115bc7Smrj 		extern int pci_allow_pseudo_children;
650ae115bc7Smrj 
651ae115bc7Smrj 		ddi_set_parent_data(child, NULL);
652ae115bc7Smrj 
653ae115bc7Smrj 		/*
654ae115bc7Smrj 		 * Try to merge the properties from this prototype
655ae115bc7Smrj 		 * node into real h/w nodes.
656ae115bc7Smrj 		 */
657ae115bc7Smrj 		if (ndi_merge_node(child, ppb_name_child) == DDI_SUCCESS) {
658ae115bc7Smrj 			/*
659ae115bc7Smrj 			 * Merged ok - return failure to remove the node.
660ae115bc7Smrj 			 */
661ae115bc7Smrj 			ddi_set_name_addr(child, NULL);
662ae115bc7Smrj 			return (DDI_FAILURE);
663ae115bc7Smrj 		}
664ae115bc7Smrj 
665ae115bc7Smrj 		/* workaround for ddivs to run under PCI */
666ae115bc7Smrj 		if (pci_allow_pseudo_children)
667ae115bc7Smrj 			return (DDI_SUCCESS);
668ae115bc7Smrj 
669ae115bc7Smrj 		/*
670ae115bc7Smrj 		 * The child was not merged into a h/w node,
671ae115bc7Smrj 		 * but there's not much we can do with it other
672ae115bc7Smrj 		 * than return failure to cause the node to be removed.
673ae115bc7Smrj 		 */
674ae115bc7Smrj 		cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
675ae115bc7Smrj 		    ddi_driver_name(child), ddi_get_name_addr(child),
676ae115bc7Smrj 		    ddi_driver_name(child));
677ae115bc7Smrj 		ddi_set_name_addr(child, NULL);
678ae115bc7Smrj 		return (DDI_NOT_WELL_FORMED);
679ae115bc7Smrj 	}
680ae115bc7Smrj 
681eae2e508Skrishnae 	ddi_set_parent_data(child, NULL);
682eae2e508Skrishnae 
683eae2e508Skrishnae 	/*
684eae2e508Skrishnae 	 * PCIe FMA specific
685eae2e508Skrishnae 	 *
686eae2e508Skrishnae 	 * Note: parent_data for parent is created only if this is PCI-E
687eae2e508Skrishnae 	 * platform, for which, SG take a different route to handle device
688eae2e508Skrishnae 	 * errors.
689eae2e508Skrishnae 	 */
690eae2e508Skrishnae 	if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
691c0da6274SZhi-Jun Robin Fu 		if (pcie_init_cfghdl(child) != DDI_SUCCESS)
692eae2e508Skrishnae 			return (DDI_FAILURE);
693fc256490SJason Beloro 		pcie_init_dom(child);
694eae2e508Skrishnae 	}
695eae2e508Skrishnae 
696ae115bc7Smrj 	/* transfer select properties from PROM to kernel */
6972df1fe9cSrandyf 	if (ddi_getprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS,
6982df1fe9cSrandyf 	    "interrupts", -1) != -1) {
699ae115bc7Smrj 		pdptr = kmem_zalloc((sizeof (struct ddi_parent_private_data) +
700ae115bc7Smrj 		    sizeof (struct intrspec)), KM_SLEEP);
701ae115bc7Smrj 		pdptr->par_intr = (struct intrspec *)(pdptr + 1);
702ae115bc7Smrj 		pdptr->par_nintr = 1;
703ae115bc7Smrj 		ddi_set_parent_data(child, pdptr);
704ae115bc7Smrj 	} else
705ae115bc7Smrj 		ddi_set_parent_data(child, NULL);
706ae115bc7Smrj 
707fc256490SJason Beloro 	if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) {
708fc256490SJason Beloro 		pcie_fini_dom(child);
709ae115bc7Smrj 		return (DDI_FAILURE);
710fc256490SJason Beloro 	}
711ae115bc7Smrj 
712ae115bc7Smrj 	/*
713ae115bc7Smrj 	 * Support for the "command-preserve" property.
714ae115bc7Smrj 	 */
715ae115bc7Smrj 	command_preserve = ddi_prop_get_int(DDI_DEV_T_ANY, child,
716ae115bc7Smrj 	    DDI_PROP_DONTPASS, "command-preserve", 0);
717ae115bc7Smrj 	command = pci_config_get16(config_handle, PCI_CONF_COMM);
718ae115bc7Smrj 	command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB);
719ae115bc7Smrj 	command |= (ppb_command_default & ~command_preserve);
720ae115bc7Smrj 	pci_config_put16(config_handle, PCI_CONF_COMM, command);
721ae115bc7Smrj 
722ae115bc7Smrj 	pci_config_teardown(&config_handle);
723ae115bc7Smrj 	return (DDI_SUCCESS);
724ae115bc7Smrj }
725ae115bc7Smrj 
726ae115bc7Smrj static void
ppb_removechild(dev_info_t * dip)727ae115bc7Smrj ppb_removechild(dev_info_t *dip)
728ae115bc7Smrj {
729ae115bc7Smrj 	struct ddi_parent_private_data *pdptr;
730eae2e508Skrishnae 	ppb_devstate_t *ppb;
731ae115bc7Smrj 
732eae2e508Skrishnae 	ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
733eae2e508Skrishnae 	    ddi_get_instance(ddi_get_parent(dip)));
734eae2e508Skrishnae 
735fc256490SJason Beloro 	if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
736fc256490SJason Beloro 		pcie_fini_dom(dip);
737c0da6274SZhi-Jun Robin Fu 		pcie_fini_cfghdl(dip);
738fc256490SJason Beloro 	} else if ((pdptr = ddi_get_parent_data(dip)) != NULL) {
739ae115bc7Smrj 		kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec)));
740ae115bc7Smrj 		ddi_set_parent_data(dip, NULL);
741ae115bc7Smrj 	}
742ae115bc7Smrj 	ddi_set_name_addr(dip, NULL);
743ae115bc7Smrj 
744ae115bc7Smrj 	/*
745ae115bc7Smrj 	 * Strip the node to properly convert it back to prototype form
746ae115bc7Smrj 	 */
747ae115bc7Smrj 	ddi_remove_minor_node(dip, NULL);
748ae115bc7Smrj 
749ae115bc7Smrj 	impl_rem_dev_props(dip);
750ae115bc7Smrj }
751ae115bc7Smrj 
752ae115bc7Smrj /*
753ae115bc7Smrj  * ppb_save_config_regs
754ae115bc7Smrj  *
755ae115bc7Smrj  * This routine saves the state of the configuration registers of all
756ae115bc7Smrj  * the child nodes of each PBM.
757ae115bc7Smrj  *
758ae115bc7Smrj  * used by: ppb_detach() on suspends
759ae115bc7Smrj  *
760ae115bc7Smrj  * return value: none
761ae115bc7Smrj  */
762ae115bc7Smrj static void
ppb_save_config_regs(ppb_devstate_t * ppb_p)763ae115bc7Smrj ppb_save_config_regs(ppb_devstate_t *ppb_p)
764ae115bc7Smrj {
765ae115bc7Smrj 	int i;
766ae115bc7Smrj 	dev_info_t *dip;
767ae115bc7Smrj 	ddi_acc_handle_t config_handle;
768ae115bc7Smrj 
769ae115bc7Smrj 	for (i = 0, dip = ddi_get_child(ppb_p->dip); dip != NULL;
770ae115bc7Smrj 	    i++, dip = ddi_get_next_sibling(dip)) {
771ae115bc7Smrj 
772ae115bc7Smrj 		if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
773ae115bc7Smrj 			cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
774ae115bc7Smrj 			    ddi_driver_name(ppb_p->dip),
775ae115bc7Smrj 			    ddi_get_instance(ppb_p->dip),
776ae115bc7Smrj 			    ddi_driver_name(dip),
777ae115bc7Smrj 			    ddi_get_instance(dip));
778ae115bc7Smrj 			continue;
779ae115bc7Smrj 		}
780ae115bc7Smrj 
781ae115bc7Smrj 		ppb_p->config_state[i].dip = dip;
782ae115bc7Smrj 		ppb_p->config_state[i].command =
783ae115bc7Smrj 		    pci_config_get16(config_handle, PCI_CONF_COMM);
784ae115bc7Smrj 		pci_config_teardown(&config_handle);
785ae115bc7Smrj 	}
786ae115bc7Smrj 	ppb_p->config_state_index = i;
787ae115bc7Smrj }
788ae115bc7Smrj 
789ae115bc7Smrj 
790ae115bc7Smrj /*
791ae115bc7Smrj  * ppb_restore_config_regs
792ae115bc7Smrj  *
793ae115bc7Smrj  * This routine restores the state of the configuration registers of all
794ae115bc7Smrj  * the child nodes of each PBM.
795ae115bc7Smrj  *
796ae115bc7Smrj  * used by: ppb_attach() on resume
797ae115bc7Smrj  *
798ae115bc7Smrj  * return value: none
799ae115bc7Smrj  */
800ae115bc7Smrj static void
ppb_restore_config_regs(ppb_devstate_t * ppb_p)801ae115bc7Smrj ppb_restore_config_regs(ppb_devstate_t *ppb_p)
802ae115bc7Smrj {
803ae115bc7Smrj 	int i;
804ae115bc7Smrj 	dev_info_t *dip;
805ae115bc7Smrj 	ddi_acc_handle_t config_handle;
806ae115bc7Smrj 
807ae115bc7Smrj 	for (i = 0; i < ppb_p->config_state_index; i++) {
808ae115bc7Smrj 		dip = ppb_p->config_state[i].dip;
809ae115bc7Smrj 		if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
810ae115bc7Smrj 			cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
811ae115bc7Smrj 			    ddi_driver_name(ppb_p->dip),
812ae115bc7Smrj 			    ddi_get_instance(ppb_p->dip),
813ae115bc7Smrj 			    ddi_driver_name(dip),
814ae115bc7Smrj 			    ddi_get_instance(dip));
815ae115bc7Smrj 			continue;
816ae115bc7Smrj 		}
817ae115bc7Smrj 		pci_config_put16(config_handle, PCI_CONF_COMM,
818ae115bc7Smrj 		    ppb_p->config_state[i].command);
819ae115bc7Smrj 		pci_config_teardown(&config_handle);
820ae115bc7Smrj 	}
821ae115bc7Smrj }
822ae115bc7Smrj 
823ae115bc7Smrj 
824ae115bc7Smrj static boolean_t
ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl)825ae115bc7Smrj ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl)
826ae115bc7Smrj {
827cb7ea99dSJimmy Vetayases 	uint16_t ptr;
828ae115bc7Smrj 
829cb7ea99dSJimmy Vetayases 	if (pci_htcap_locate(cfg_hdl,
830cb7ea99dSJimmy Vetayases 	    PCI_HTCAP_TYPE_MASK | PCI_HTCAP_MSIMAP_ENABLE_MASK,
831cb7ea99dSJimmy Vetayases 	    PCI_HTCAP_MSIMAP_TYPE | PCI_HTCAP_MSIMAP_ENABLE, &ptr) !=
832cb7ea99dSJimmy Vetayases 	    DDI_SUCCESS)
833ae115bc7Smrj 		return (B_FALSE);
834ae115bc7Smrj 
835ae115bc7Smrj 	return (B_TRUE);
836ae115bc7Smrj }
837ae115bc7Smrj 
838ae115bc7Smrj 
839ae115bc7Smrj static int
ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl,int cmd)840ae115bc7Smrj ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd)
841ae115bc7Smrj {
842cb7ea99dSJimmy Vetayases 	uint16_t ptr;
843ae115bc7Smrj 	uint16_t reg;
844ae115bc7Smrj 
845cb7ea99dSJimmy Vetayases 	if (pci_htcap_locate(cfg_hdl, PCI_HTCAP_TYPE_MASK,
846cb7ea99dSJimmy Vetayases 	    PCI_HTCAP_MSIMAP_TYPE, &ptr) != DDI_SUCCESS)
847ae115bc7Smrj 		return (0);
848ae115bc7Smrj 
849ae115bc7Smrj 	reg = pci_config_get16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF);
850ae115bc7Smrj 	switch (cmd) {
851ae115bc7Smrj 	case HT_MSIMAP_ENABLE:
852cb7ea99dSJimmy Vetayases 		reg |= PCI_HTCAP_MSIMAP_ENABLE;
853ae115bc7Smrj 		break;
854ae115bc7Smrj 	case HT_MSIMAP_DISABLE:
855ae115bc7Smrj 	default:
856cb7ea99dSJimmy Vetayases 		reg &= ~(uint16_t)PCI_HTCAP_MSIMAP_ENABLE;
857ae115bc7Smrj 	}
858ae115bc7Smrj 
859ae115bc7Smrj 	pci_config_put16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF, reg);
860ae115bc7Smrj 	return (1);
861ae115bc7Smrj }
862ae115bc7Smrj 
863ae115bc7Smrj 
864ae115bc7Smrj /*
865ae115bc7Smrj  * intercept certain interrupt services to handle special cases
866ae115bc7Smrj  */
867ae115bc7Smrj static int
ppb_intr_ops(dev_info_t * pdip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)868ae115bc7Smrj ppb_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
869ae115bc7Smrj     ddi_intr_handle_impl_t *hdlp, void *result)
870ae115bc7Smrj {
871ae115bc7Smrj 	ddi_acc_handle_t cfg_hdl;
872ae115bc7Smrj 	int rv = DDI_SUCCESS;
873ae115bc7Smrj 
874ae115bc7Smrj 	if (intr_op != DDI_INTROP_SUPPORTED_TYPES)
875ae115bc7Smrj 		return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result));
876ae115bc7Smrj 
877ae115bc7Smrj 	DDI_INTR_NEXDBG((CE_CONT,
878ae115bc7Smrj 	    "ppb_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
879ae115bc7Smrj 	    (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
880ae115bc7Smrj 
881ae115bc7Smrj 	/* Fixed interrupt is supported by default */
882ae115bc7Smrj 	*(int *)result = DDI_INTR_TYPE_FIXED;
883ae115bc7Smrj 
884ae115bc7Smrj 	if (ppb_support_msi == -1) {
885ae115bc7Smrj 		DDI_INTR_NEXDBG((CE_CONT,
886ae115bc7Smrj 		    "ppb_intr_ops: MSI is not allowed\n"));
887ae115bc7Smrj 		goto OUT;
888ae115bc7Smrj 	}
889ae115bc7Smrj 
890ae115bc7Smrj 	if (ppb_support_msi == 1) {
891ae115bc7Smrj 		DDI_INTR_NEXDBG((CE_CONT,
892ae115bc7Smrj 		    "ppb_intr_ops: MSI is always allowed\n"));
893ae115bc7Smrj 		rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result);
894ae115bc7Smrj 		goto OUT;
895ae115bc7Smrj 	}
896ae115bc7Smrj 
897ae115bc7Smrj 	if (pci_config_setup(pdip, &cfg_hdl) != DDI_SUCCESS) {
898ae115bc7Smrj 		DDI_INTR_NEXDBG((CE_CONT,
899ae115bc7Smrj 		    "ppb_intr_ops: pci_config_setup() failed\n"));
900ae115bc7Smrj 		goto OUT;
901ae115bc7Smrj 	}
902ae115bc7Smrj 
903ae115bc7Smrj 	/*
904ae115bc7Smrj 	 * check for hypertransport msi mapping capability
905ae115bc7Smrj 	 */
906ae115bc7Smrj 	if (ppb_ht_msimap_check(cfg_hdl)) {
907ae115bc7Smrj 		DDI_INTR_NEXDBG((CE_CONT,
908ae115bc7Smrj 		    "ppb_intr_ops: HT MSI mapping enabled\n"));
909ae115bc7Smrj 		rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result);
910ae115bc7Smrj 	}
911ae115bc7Smrj 
912ae115bc7Smrj 	/*
913ae115bc7Smrj 	 * if we add failure conditions after pci_config_setup, move this to
914ae115bc7Smrj 	 * OUT and use an extra flag to indicate the need to teardown cfg_hdl
915ae115bc7Smrj 	 */
916ae115bc7Smrj 	pci_config_teardown(&cfg_hdl);
917ae115bc7Smrj 
918ae115bc7Smrj OUT:
919ae115bc7Smrj 	DDI_INTR_NEXDBG((CE_CONT,
920ae115bc7Smrj 	    "ppb_intr_ops: rdip 0x%p, returns supported types: 0x%x\n",
921ae115bc7Smrj 	    (void *)rdip, *(int *)result));
922ae115bc7Smrj 	return (rv);
923ae115bc7Smrj }
924ae115bc7Smrj 
92526947304SEvan Yan /* ARGSUSED */
926ae115bc7Smrj static int
ppb_open(dev_t * devp,int flags,int otyp,cred_t * credp)927ae115bc7Smrj ppb_open(dev_t *devp, int flags, int otyp, cred_t *credp)
928ae115bc7Smrj {
92926947304SEvan Yan 	int		instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(*devp));
93026947304SEvan Yan 	ppb_devstate_t	*ppb_p = ddi_get_soft_state(ppb_state, instance);
93126947304SEvan Yan 	int	rv;
93226947304SEvan Yan 
93326947304SEvan Yan 	if (ppb_p == NULL)
93426947304SEvan Yan 		return (ENXIO);
93526947304SEvan Yan 
93626947304SEvan Yan 	/*
93726947304SEvan Yan 	 * Ioctls will be handled by PCI Express framework for all
93826947304SEvan Yan 	 * PCIe platforms
93926947304SEvan Yan 	 */
94026947304SEvan Yan 	if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
94126947304SEvan Yan 		mutex_enter(&ppb_p->ppb_mutex);
94226947304SEvan Yan 		rv = pcie_open(ppb_p->dip, devp, flags, otyp, credp);
94326947304SEvan Yan 		mutex_exit(&ppb_p->ppb_mutex);
94426947304SEvan Yan 		return (rv);
94526947304SEvan Yan 	}
94626947304SEvan Yan 
947ae115bc7Smrj 	return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp));
948ae115bc7Smrj }
949ae115bc7Smrj 
95026947304SEvan Yan /* ARGSUSED */
951ae115bc7Smrj static int
ppb_close(dev_t dev,int flags,int otyp,cred_t * credp)952ae115bc7Smrj ppb_close(dev_t dev, int flags, int otyp, cred_t *credp)
953ae115bc7Smrj {
95426947304SEvan Yan 	int		instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
95526947304SEvan Yan 	ppb_devstate_t	*ppb_p = ddi_get_soft_state(ppb_state, instance);
95626947304SEvan Yan 	int	rv;
95726947304SEvan Yan 
95826947304SEvan Yan 	if (ppb_p == NULL)
95926947304SEvan Yan 		return (ENXIO);
96026947304SEvan Yan 
96126947304SEvan Yan 	mutex_enter(&ppb_p->ppb_mutex);
96226947304SEvan Yan 	/*
96326947304SEvan Yan 	 * Ioctls will be handled by PCI Express framework for all
96426947304SEvan Yan 	 * PCIe platforms
96526947304SEvan Yan 	 */
96626947304SEvan Yan 	if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
96726947304SEvan Yan 		rv = pcie_close(ppb_p->dip, dev, flags, otyp, credp);
96826947304SEvan Yan 		mutex_exit(&ppb_p->ppb_mutex);
96926947304SEvan Yan 		return (rv);
97026947304SEvan Yan 	}
97126947304SEvan Yan 
97226947304SEvan Yan 	mutex_exit(&ppb_p->ppb_mutex);
973ae115bc7Smrj 	return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp));
974ae115bc7Smrj }
975ae115bc7Smrj 
97626947304SEvan Yan /*
97726947304SEvan Yan  * ppb_ioctl: devctl hotplug controls
97826947304SEvan Yan  */
97926947304SEvan Yan /* ARGSUSED */
980ae115bc7Smrj static int
ppb_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)98126947304SEvan Yan ppb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
98226947304SEvan Yan 	int *rvalp)
983ae115bc7Smrj {
98426947304SEvan Yan 	int		instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
98526947304SEvan Yan 	ppb_devstate_t	*ppb_p = ddi_get_soft_state(ppb_state, instance);
98626947304SEvan Yan 
98726947304SEvan Yan 	if (ppb_p == NULL)
98826947304SEvan Yan 		return (ENXIO);
98926947304SEvan Yan 
99026947304SEvan Yan 	/*
99126947304SEvan Yan 	 * Ioctls will be handled by PCI Express framework for all
99226947304SEvan Yan 	 * PCIe platforms
99326947304SEvan Yan 	 */
99426947304SEvan Yan 	if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
99526947304SEvan Yan 		return (pcie_ioctl(ppb_p->dip, dev, cmd, arg, mode, credp,
99626947304SEvan Yan 		    rvalp));
99726947304SEvan Yan 
998ae115bc7Smrj 	return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, credp,
999ae115bc7Smrj 	    rvalp));
1000ae115bc7Smrj }
1001ae115bc7Smrj 
1002ae115bc7Smrj static int
ppb_prop_op(dev_t dev,dev_info_t * dip,ddi_prop_op_t prop_op,int flags,char * name,caddr_t valuep,int * lengthp)100326947304SEvan Yan ppb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int flags,
100426947304SEvan Yan     char *name, caddr_t valuep, int *lengthp)
1005ae115bc7Smrj {
100626947304SEvan Yan 	int		instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
100726947304SEvan Yan 	ppb_devstate_t	*ppb_p = ddi_get_soft_state(ppb_state, instance);
100826947304SEvan Yan 
100926947304SEvan Yan 	if (ppb_p == NULL)
101026947304SEvan Yan 		return (ENXIO);
101126947304SEvan Yan 
101226947304SEvan Yan 	if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
101326947304SEvan Yan 		return (pcie_prop_op(dev, dip, prop_op, flags, name,
101426947304SEvan Yan 		    valuep, lengthp));
101526947304SEvan Yan 
1016ae115bc7Smrj 	return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags,
1017ae115bc7Smrj 	    name, valuep, lengthp));
1018ae115bc7Smrj }
1019ae115bc7Smrj 
1020ae115bc7Smrj static int
ppb_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)1021ae115bc7Smrj ppb_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
1022ae115bc7Smrj {
102326947304SEvan Yan 	minor_t		minor = getminor((dev_t)arg);
102426947304SEvan Yan 	int		instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
102526947304SEvan Yan 	ppb_devstate_t	*ppb_p = ddi_get_soft_state(ppb_state, instance);
102626947304SEvan Yan 
102726947304SEvan Yan 	if (ppb_p == NULL)
102826947304SEvan Yan 		return (DDI_FAILURE);
102926947304SEvan Yan 
103026947304SEvan Yan 	if (ppb_p->parent_bus != PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
1031ae115bc7Smrj 		return (pcihp_info(dip, cmd, arg, result));
103226947304SEvan Yan 
103326947304SEvan Yan 	switch (cmd) {
103426947304SEvan Yan 	default:
103526947304SEvan Yan 		return (DDI_FAILURE);
103626947304SEvan Yan 
103726947304SEvan Yan 	case DDI_INFO_DEVT2INSTANCE:
103826947304SEvan Yan 		*result = (void *)(uintptr_t)instance;
103926947304SEvan Yan 		return (DDI_SUCCESS);
104026947304SEvan Yan 
104126947304SEvan Yan 	case DDI_INFO_DEVT2DEVINFO:
104226947304SEvan Yan 		if (ppb_p == NULL)
104326947304SEvan Yan 			return (DDI_FAILURE);
104426947304SEvan Yan 		*result = (void *)ppb_p->dip;
104526947304SEvan Yan 		return (DDI_SUCCESS);
104626947304SEvan Yan 	}
1047ae115bc7Smrj }
1048ae115bc7Smrj 
ppb_peekpoke_cb(dev_info_t * dip,ddi_fm_error_t * derr)1049eae2e508Skrishnae void ppb_peekpoke_cb(dev_info_t *dip, ddi_fm_error_t *derr) {
1050eae2e508Skrishnae 	(void) pci_ereport_post(dip, derr, NULL);
1051eae2e508Skrishnae }
1052eae2e508Skrishnae 
1053ae115bc7Smrj /*ARGSUSED*/
1054ae115bc7Smrj static int
ppb_fm_init(dev_info_t * dip,dev_info_t * tdip,int cap,ddi_iblock_cookie_t * ibc)1055ae115bc7Smrj ppb_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap,
1056ae115bc7Smrj     ddi_iblock_cookie_t *ibc)
1057ae115bc7Smrj {
1058ae115bc7Smrj 	ppb_devstate_t  *ppb = ddi_get_soft_state(ppb_state,
1059ae115bc7Smrj 	    ddi_get_instance(dip));
1060ae115bc7Smrj 
1061ae115bc7Smrj 	ASSERT(ibc != NULL);
1062ae115bc7Smrj 	*ibc = ppb->ppb_fm_ibc;
1063ae115bc7Smrj 
1064ae115bc7Smrj 	return (ppb->ppb_fmcap);
1065ae115bc7Smrj }
1066ae115bc7Smrj 
1067ae115bc7Smrj /*ARGSUSED*/
1068ae115bc7Smrj static int
ppb_fm_callback(dev_info_t * dip,ddi_fm_error_t * derr,const void * no_used)1069ae115bc7Smrj ppb_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *no_used)
1070ae115bc7Smrj {
1071ae115bc7Smrj 	ppb_devstate_t  *ppb = ddi_get_soft_state(ppb_state,
1072ae115bc7Smrj 	    ddi_get_instance(dip));
1073ae115bc7Smrj 
1074ae115bc7Smrj 	mutex_enter(&ppb->ppb_err_mutex);
1075ae115bc7Smrj 	pci_ereport_post(dip, derr, NULL);
1076ae115bc7Smrj 	mutex_exit(&ppb->ppb_err_mutex);
1077ae115bc7Smrj 	return (derr->fme_status);
1078ae115bc7Smrj }
1079