xref: /onnv-gate/usr/src/uts/intel/io/pci/pci_pci.c (revision 3446)
1*3446Smrj /*
2*3446Smrj  * CDDL HEADER START
3*3446Smrj  *
4*3446Smrj  * The contents of this file are subject to the terms of the
5*3446Smrj  * Common Development and Distribution License (the "License").
6*3446Smrj  * You may not use this file except in compliance with the License.
7*3446Smrj  *
8*3446Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*3446Smrj  * or http://www.opensolaris.org/os/licensing.
10*3446Smrj  * See the License for the specific language governing permissions
11*3446Smrj  * and limitations under the License.
12*3446Smrj  *
13*3446Smrj  * When distributing Covered Code, include this CDDL HEADER in each
14*3446Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*3446Smrj  * If applicable, add the following below this CDDL HEADER, with the
16*3446Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
17*3446Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
18*3446Smrj  *
19*3446Smrj  * CDDL HEADER END
20*3446Smrj  */
21*3446Smrj /*
22*3446Smrj  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23*3446Smrj  * Use is subject to license terms.
24*3446Smrj  */
25*3446Smrj 
26*3446Smrj #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*3446Smrj 
28*3446Smrj /*
29*3446Smrj  * PCI to PCI bus bridge nexus driver
30*3446Smrj  */
31*3446Smrj 
32*3446Smrj #include <sys/conf.h>
33*3446Smrj #include <sys/kmem.h>
34*3446Smrj #include <sys/debug.h>
35*3446Smrj #include <sys/modctl.h>
36*3446Smrj #include <sys/autoconf.h>
37*3446Smrj #include <sys/ddi_impldefs.h>
38*3446Smrj #include <sys/pci.h>
39*3446Smrj #include <sys/ddi.h>
40*3446Smrj #include <sys/sunddi.h>
41*3446Smrj #include <sys/sunndi.h>
42*3446Smrj #include <sys/ddifm.h>
43*3446Smrj #include <sys/ndifm.h>
44*3446Smrj #include <sys/fm/protocol.h>
45*3446Smrj #include <sys/hotplug/pci/pcihp.h>
46*3446Smrj #include <sys/pci_intr_lib.h>
47*3446Smrj #include <sys/psm.h>
48*3446Smrj 
49*3446Smrj /*
50*3446Smrj  * The variable controls the default setting of the command register
51*3446Smrj  * for pci devices.  See ppb_initchild() for details.
52*3446Smrj  */
53*3446Smrj static ushort_t ppb_command_default = PCI_COMM_ME | PCI_COMM_MAE | PCI_COMM_IO;
54*3446Smrj 
55*3446Smrj 
56*3446Smrj static int	ppb_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *,
57*3446Smrj 		    off_t, off_t, caddr_t *);
58*3446Smrj static int	ppb_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
59*3446Smrj 		    void *, void *);
60*3446Smrj static int	ppb_fm_init(dev_info_t *, dev_info_t *, int,
61*3446Smrj 		    ddi_iblock_cookie_t *);
62*3446Smrj static int	ppb_fm_callback(dev_info_t *, ddi_fm_error_t *, const void *);
63*3446Smrj static int	ppb_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t,
64*3446Smrj 		    ddi_intr_handle_impl_t *, void *);
65*3446Smrj 
66*3446Smrj /*
67*3446Smrj  * ppb_support_msi: Flag that controls MSI support across P2P Bridges.
68*3446Smrj  * By default, MSI is not supported except for special cases like HT
69*3446Smrj  * bridges/tunnels that have HT MSI mapping enabled.
70*3446Smrj  *
71*3446Smrj  * However, MSI support behavior can be patched on a system by changing
72*3446Smrj  * the value of this flag as shown below:-
73*3446Smrj  *	 0 = default value, MSI is allowed by this driver for special cases
74*3446Smrj  *	 1 = MSI supported without any checks for this driver
75*3446Smrj  *	-1 = MSI not supported at all
76*3446Smrj  */
77*3446Smrj int ppb_support_msi = 0;
78*3446Smrj 
79*3446Smrj /*
80*3446Smrj  * Controls the usage of the Hypertransport MSI mapping capability
81*3446Smrj  *	0 = default value, leave hardware function as it is
82*3446Smrj  *	1 = always enable HT MSI mapping
83*3446Smrj  *     -1 = always disable HT MSI mapping
84*3446Smrj  */
85*3446Smrj int ppb_support_ht_msimap = 0;
86*3446Smrj 
87*3446Smrj /*
88*3446Smrj  * masks and values for the upper 16-bits of hypertransport cap headers
89*3446Smrj  */
90*3446Smrj #define	PCI_CAP_HT_MSIMAP_TYPE			0xA800
91*3446Smrj #define	PCI_CAP_HT_MSIMAP_TYPE_MASK		0xFF00
92*3446Smrj #define	PCI_CAP_HT_MSIMAP_ENABLE		0x0001
93*3446Smrj #define	PCI_CAP_HT_MSIMAP_ENABLE_MASK		0x0001
94*3446Smrj 
95*3446Smrj 
96*3446Smrj struct bus_ops ppb_bus_ops = {
97*3446Smrj 	BUSO_REV,
98*3446Smrj 	ppb_bus_map,
99*3446Smrj 	0,
100*3446Smrj 	0,
101*3446Smrj 	0,
102*3446Smrj 	i_ddi_map_fault,
103*3446Smrj 	ddi_dma_map,
104*3446Smrj 	ddi_dma_allochdl,
105*3446Smrj 	ddi_dma_freehdl,
106*3446Smrj 	ddi_dma_bindhdl,
107*3446Smrj 	ddi_dma_unbindhdl,
108*3446Smrj 	ddi_dma_flush,
109*3446Smrj 	ddi_dma_win,
110*3446Smrj 	ddi_dma_mctl,
111*3446Smrj 	ppb_ctlops,
112*3446Smrj 	ddi_bus_prop_op,
113*3446Smrj 	0,		/* (*bus_get_eventcookie)();	*/
114*3446Smrj 	0,		/* (*bus_add_eventcall)();	*/
115*3446Smrj 	0,		/* (*bus_remove_eventcall)();	*/
116*3446Smrj 	0,		/* (*bus_post_event)();		*/
117*3446Smrj 	0,		/* (*bus_intr_ctl)();		*/
118*3446Smrj 	0,		/* (*bus_config)(); 		*/
119*3446Smrj 	0,		/* (*bus_unconfig)(); 		*/
120*3446Smrj 	ppb_fm_init,	/* (*bus_fm_init)(); 		*/
121*3446Smrj 	NULL,		/* (*bus_fm_fini)(); 		*/
122*3446Smrj 	NULL,		/* (*bus_fm_access_enter)(); 	*/
123*3446Smrj 	NULL,		/* (*bus_fm_access_exit)(); 	*/
124*3446Smrj 	NULL,		/* (*bus_power)(); 	*/
125*3446Smrj 	ppb_intr_ops	/* (*bus_intr_op)(); 		*/
126*3446Smrj };
127*3446Smrj 
128*3446Smrj /*
129*3446Smrj  * The goal here is to leverage off of the pcihp.c source without making
130*3446Smrj  * changes to it.  Call into it's cb_ops directly if needed.
131*3446Smrj  */
132*3446Smrj static int	ppb_open(dev_t *, int, int, cred_t *);
133*3446Smrj static int	ppb_close(dev_t, int, int, cred_t *);
134*3446Smrj static int	ppb_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
135*3446Smrj static int	ppb_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
136*3446Smrj 		    caddr_t, int *);
137*3446Smrj static int	ppb_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
138*3446Smrj 
139*3446Smrj struct cb_ops ppb_cb_ops = {
140*3446Smrj 	ppb_open,			/* open */
141*3446Smrj 	ppb_close,			/* close */
142*3446Smrj 	nodev,				/* strategy */
143*3446Smrj 	nodev,				/* print */
144*3446Smrj 	nodev,				/* dump */
145*3446Smrj 	nodev,				/* read */
146*3446Smrj 	nodev,				/* write */
147*3446Smrj 	ppb_ioctl,			/* ioctl */
148*3446Smrj 	nodev,				/* devmap */
149*3446Smrj 	nodev,				/* mmap */
150*3446Smrj 	nodev,				/* segmap */
151*3446Smrj 	nochpoll,			/* poll */
152*3446Smrj 	ppb_prop_op,			/* cb_prop_op */
153*3446Smrj 	NULL,				/* streamtab */
154*3446Smrj 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
155*3446Smrj 	CB_REV,				/* rev */
156*3446Smrj 	nodev,				/* int (*cb_aread)() */
157*3446Smrj 	nodev				/* int (*cb_awrite)() */
158*3446Smrj };
159*3446Smrj 
160*3446Smrj 
161*3446Smrj static int ppb_probe(dev_info_t *);
162*3446Smrj static int ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
163*3446Smrj static int ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
164*3446Smrj 
165*3446Smrj struct dev_ops ppb_ops = {
166*3446Smrj 	DEVO_REV,		/* devo_rev */
167*3446Smrj 	0,			/* refcnt  */
168*3446Smrj 	ppb_info,		/* info */
169*3446Smrj 	nulldev,		/* identify */
170*3446Smrj 	ppb_probe,		/* probe */
171*3446Smrj 	ppb_attach,		/* attach */
172*3446Smrj 	ppb_detach,		/* detach */
173*3446Smrj 	nulldev,		/* reset */
174*3446Smrj 	&ppb_cb_ops,		/* driver operations */
175*3446Smrj 	&ppb_bus_ops		/* bus operations */
176*3446Smrj };
177*3446Smrj 
178*3446Smrj /*
179*3446Smrj  * Module linkage information for the kernel.
180*3446Smrj  */
181*3446Smrj 
182*3446Smrj static struct modldrv modldrv = {
183*3446Smrj 	&mod_driverops, /* Type of module */
184*3446Smrj 	"PCI to PCI bridge nexus driver %I%",
185*3446Smrj 	&ppb_ops,	/* driver ops */
186*3446Smrj };
187*3446Smrj 
188*3446Smrj static struct modlinkage modlinkage = {
189*3446Smrj 	MODREV_1,
190*3446Smrj 	(void *)&modldrv,
191*3446Smrj 	NULL
192*3446Smrj };
193*3446Smrj 
194*3446Smrj /*
195*3446Smrj  * soft state pointer and structure template:
196*3446Smrj  */
197*3446Smrj static void *ppb_state;
198*3446Smrj 
199*3446Smrj typedef struct {
200*3446Smrj 	dev_info_t *dip;
201*3446Smrj 	int ppb_fmcap;
202*3446Smrj 	ddi_iblock_cookie_t ppb_fm_ibc;
203*3446Smrj 	kmutex_t ppb_peek_poke_mutex;
204*3446Smrj 	kmutex_t ppb_err_mutex;
205*3446Smrj 
206*3446Smrj 	/*
207*3446Smrj 	 * cpr support:
208*3446Smrj 	 */
209*3446Smrj 	uint_t config_state_index;
210*3446Smrj 	struct {
211*3446Smrj 		dev_info_t *dip;
212*3446Smrj 		ushort_t command;
213*3446Smrj 		uchar_t cache_line_size;
214*3446Smrj 		uchar_t latency_timer;
215*3446Smrj 		uchar_t header_type;
216*3446Smrj 		uchar_t sec_latency_timer;
217*3446Smrj 		ushort_t bridge_control;
218*3446Smrj 	} config_state[PCI_MAX_CHILDREN];
219*3446Smrj } ppb_devstate_t;
220*3446Smrj 
221*3446Smrj 
222*3446Smrj /*
223*3446Smrj  * forward function declarations:
224*3446Smrj  */
225*3446Smrj static void	ppb_removechild(dev_info_t *);
226*3446Smrj static int	ppb_initchild(dev_info_t *child);
227*3446Smrj static void	ppb_save_config_regs(ppb_devstate_t *ppb_p);
228*3446Smrj static void	ppb_restore_config_regs(ppb_devstate_t *ppb_p);
229*3446Smrj static uint8_t	ppb_find_ht_cap(ddi_acc_handle_t cfg_hdl, uint16_t reg_mask,
230*3446Smrj 		    uint16_t reg_val);
231*3446Smrj static boolean_t	ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl);
232*3446Smrj static int	ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd);
233*3446Smrj 
234*3446Smrj /*
235*3446Smrj  * for <cmd> in ppb_ht_msimap_set
236*3446Smrj  */
237*3446Smrj #define	HT_MSIMAP_ENABLE	1
238*3446Smrj #define	HT_MSIMAP_DISABLE	0
239*3446Smrj 
240*3446Smrj 
241*3446Smrj int
242*3446Smrj _init(void)
243*3446Smrj {
244*3446Smrj 	int e;
245*3446Smrj 
246*3446Smrj 	if ((e = ddi_soft_state_init(&ppb_state, sizeof (ppb_devstate_t),
247*3446Smrj 	    1)) == 0 && (e = mod_install(&modlinkage)) != 0)
248*3446Smrj 		ddi_soft_state_fini(&ppb_state);
249*3446Smrj 	return (e);
250*3446Smrj }
251*3446Smrj 
252*3446Smrj int
253*3446Smrj _fini(void)
254*3446Smrj {
255*3446Smrj 	int e;
256*3446Smrj 
257*3446Smrj 	if ((e = mod_remove(&modlinkage)) == 0)
258*3446Smrj 		ddi_soft_state_fini(&ppb_state);
259*3446Smrj 	return (e);
260*3446Smrj }
261*3446Smrj 
262*3446Smrj int
263*3446Smrj _info(struct modinfo *modinfop)
264*3446Smrj {
265*3446Smrj 	return (mod_info(&modlinkage, modinfop));
266*3446Smrj }
267*3446Smrj 
268*3446Smrj /*ARGSUSED*/
269*3446Smrj static int
270*3446Smrj ppb_probe(dev_info_t *devi)
271*3446Smrj {
272*3446Smrj 	return (DDI_PROBE_SUCCESS);
273*3446Smrj }
274*3446Smrj 
275*3446Smrj /*ARGSUSED*/
276*3446Smrj static int
277*3446Smrj ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
278*3446Smrj {
279*3446Smrj 	int instance;
280*3446Smrj 	ppb_devstate_t *ppb;
281*3446Smrj 	ddi_acc_handle_t config_handle;
282*3446Smrj 
283*3446Smrj 	switch (cmd) {
284*3446Smrj 	case DDI_ATTACH:
285*3446Smrj 
286*3446Smrj 		/*
287*3446Smrj 		 * Make sure the "device_type" property exists.
288*3446Smrj 		 */
289*3446Smrj 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
290*3446Smrj 		    "device_type", "pci");
291*3446Smrj 
292*3446Smrj 		/*
293*3446Smrj 		 * Allocate and get soft state structure.
294*3446Smrj 		 */
295*3446Smrj 		instance = ddi_get_instance(devi);
296*3446Smrj 		if (ddi_soft_state_zalloc(ppb_state, instance) != DDI_SUCCESS)
297*3446Smrj 			return (DDI_FAILURE);
298*3446Smrj 		ppb = ddi_get_soft_state(ppb_state, instance);
299*3446Smrj 		ppb->dip = devi;
300*3446Smrj 
301*3446Smrj 		/*
302*3446Smrj 		 * don't enable ereports if immediate child of npe
303*3446Smrj 		 */
304*3446Smrj 		if (strcmp(ddi_driver_name(ddi_get_parent(devi)), "npe") == 0)
305*3446Smrj 			ppb->ppb_fmcap = DDI_FM_ERRCB_CAPABLE |
306*3446Smrj 			    DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
307*3446Smrj 		else
308*3446Smrj 			ppb->ppb_fmcap = DDI_FM_EREPORT_CAPABLE |
309*3446Smrj 			    DDI_FM_ERRCB_CAPABLE | DDI_FM_ACCCHK_CAPABLE |
310*3446Smrj 			    DDI_FM_DMACHK_CAPABLE;
311*3446Smrj 
312*3446Smrj 		ddi_fm_init(devi, &ppb->ppb_fmcap, &ppb->ppb_fm_ibc);
313*3446Smrj 		mutex_init(&ppb->ppb_err_mutex, NULL, MUTEX_DRIVER,
314*3446Smrj 		    (void *)ppb->ppb_fm_ibc);
315*3446Smrj 		mutex_init(&ppb->ppb_peek_poke_mutex, NULL, MUTEX_DRIVER,
316*3446Smrj 		    (void *)ppb->ppb_fm_ibc);
317*3446Smrj 
318*3446Smrj 		if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
319*3446Smrj 		    DDI_FM_EREPORT_CAPABLE))
320*3446Smrj 			pci_ereport_setup(devi);
321*3446Smrj 		if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
322*3446Smrj 		    ddi_fm_handler_register(devi, ppb_fm_callback, NULL);
323*3446Smrj 
324*3446Smrj 		if (pci_config_setup(devi, &config_handle) != DDI_SUCCESS) {
325*3446Smrj 			if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
326*3446Smrj 				ddi_fm_handler_unregister(devi);
327*3446Smrj 			if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
328*3446Smrj 			    DDI_FM_EREPORT_CAPABLE))
329*3446Smrj 				pci_ereport_teardown(devi);
330*3446Smrj 			ddi_fm_fini(devi);
331*3446Smrj 			ddi_soft_state_free(ppb_state, instance);
332*3446Smrj 			return (DDI_FAILURE);
333*3446Smrj 		}
334*3446Smrj 
335*3446Smrj 		if (ppb_support_ht_msimap == 1)
336*3446Smrj 			(void) ppb_ht_msimap_set(config_handle,
337*3446Smrj 			    HT_MSIMAP_ENABLE);
338*3446Smrj 		else if (ppb_support_ht_msimap == -1)
339*3446Smrj 			(void) ppb_ht_msimap_set(config_handle,
340*3446Smrj 			    HT_MSIMAP_DISABLE);
341*3446Smrj 
342*3446Smrj 		pci_config_teardown(&config_handle);
343*3446Smrj 
344*3446Smrj 		/*
345*3446Smrj 		 * Initialize hotplug support on this bus. At minimum
346*3446Smrj 		 * (for non hotplug bus) this would create ":devctl" minor
347*3446Smrj 		 * node to support DEVCTL_DEVICE_* and DEVCTL_BUS_* ioctls
348*3446Smrj 		 * to this bus.
349*3446Smrj 		 */
350*3446Smrj 		if (pcihp_init(devi) != DDI_SUCCESS)
351*3446Smrj 		    cmn_err(CE_WARN, "pci: Failed to setup hotplug framework");
352*3446Smrj 
353*3446Smrj 		ddi_report_dev(devi);
354*3446Smrj 		return (DDI_SUCCESS);
355*3446Smrj 
356*3446Smrj 	case DDI_RESUME:
357*3446Smrj 
358*3446Smrj 		/*
359*3446Smrj 		 * Get the soft state structure for the bridge.
360*3446Smrj 		 */
361*3446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
362*3446Smrj 		ppb_restore_config_regs(ppb);
363*3446Smrj 		return (DDI_SUCCESS);
364*3446Smrj 
365*3446Smrj 	default:
366*3446Smrj 		break;
367*3446Smrj 	}
368*3446Smrj 	return (DDI_FAILURE);
369*3446Smrj }
370*3446Smrj 
371*3446Smrj /*ARGSUSED*/
372*3446Smrj static int
373*3446Smrj ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
374*3446Smrj {
375*3446Smrj 	ppb_devstate_t *ppb;
376*3446Smrj 
377*3446Smrj 	switch (cmd) {
378*3446Smrj 	case DDI_DETACH:
379*3446Smrj 		(void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type");
380*3446Smrj 
381*3446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
382*3446Smrj 		if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
383*3446Smrj 			ddi_fm_handler_unregister(devi);
384*3446Smrj 		if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
385*3446Smrj 		    DDI_FM_EREPORT_CAPABLE))
386*3446Smrj 			pci_ereport_teardown(devi);
387*3446Smrj 		mutex_destroy(&ppb->ppb_peek_poke_mutex);
388*3446Smrj 		mutex_destroy(&ppb->ppb_err_mutex);
389*3446Smrj 		ddi_fm_fini(devi);
390*3446Smrj 
391*3446Smrj 		/*
392*3446Smrj 		 * And finally free the per-pci soft state.
393*3446Smrj 		 */
394*3446Smrj 		ddi_soft_state_free(ppb_state, ddi_get_instance(devi));
395*3446Smrj 
396*3446Smrj 		/*
397*3446Smrj 		 * Uninitialize hotplug support on this bus.
398*3446Smrj 		 */
399*3446Smrj 		(void) pcihp_uninit(devi);
400*3446Smrj 		return (DDI_SUCCESS);
401*3446Smrj 
402*3446Smrj 	case DDI_SUSPEND:
403*3446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
404*3446Smrj 		ppb_save_config_regs(ppb);
405*3446Smrj 		return (DDI_SUCCESS);
406*3446Smrj 
407*3446Smrj 	default:
408*3446Smrj 		break;
409*3446Smrj 	}
410*3446Smrj 	return (DDI_FAILURE);
411*3446Smrj }
412*3446Smrj 
413*3446Smrj /*ARGSUSED*/
414*3446Smrj static int
415*3446Smrj ppb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
416*3446Smrj 	off_t offset, off_t len, caddr_t *vaddrp)
417*3446Smrj {
418*3446Smrj 	dev_info_t *pdip;
419*3446Smrj 
420*3446Smrj 	pdip = (dev_info_t *)DEVI(dip)->devi_parent;
421*3446Smrj 	return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip,
422*3446Smrj 			rdip, mp, offset, len, vaddrp));
423*3446Smrj }
424*3446Smrj 
425*3446Smrj /*ARGSUSED*/
426*3446Smrj static int
427*3446Smrj ppb_ctlops(dev_info_t *dip, dev_info_t *rdip,
428*3446Smrj 	ddi_ctl_enum_t ctlop, void *arg, void *result)
429*3446Smrj {
430*3446Smrj 	pci_regspec_t *drv_regp;
431*3446Smrj 	int	reglen;
432*3446Smrj 	int	rn;
433*3446Smrj 	int	totreg;
434*3446Smrj 	ppb_devstate_t *ppb;
435*3446Smrj 
436*3446Smrj 	switch (ctlop) {
437*3446Smrj 	case DDI_CTLOPS_REPORTDEV:
438*3446Smrj 		if (rdip == (dev_info_t *)0)
439*3446Smrj 			return (DDI_FAILURE);
440*3446Smrj 		cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n",
441*3446Smrj 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
442*3446Smrj 		    ddi_driver_name(rdip),
443*3446Smrj 		    ddi_get_instance(rdip));
444*3446Smrj 		return (DDI_SUCCESS);
445*3446Smrj 
446*3446Smrj 	case DDI_CTLOPS_INITCHILD:
447*3446Smrj 		return (ppb_initchild((dev_info_t *)arg));
448*3446Smrj 
449*3446Smrj 	case DDI_CTLOPS_UNINITCHILD:
450*3446Smrj 		ppb_removechild((dev_info_t *)arg);
451*3446Smrj 		return (DDI_SUCCESS);
452*3446Smrj 
453*3446Smrj 	case DDI_CTLOPS_SIDDEV:
454*3446Smrj 		return (DDI_SUCCESS);
455*3446Smrj 
456*3446Smrj 	case DDI_CTLOPS_REGSIZE:
457*3446Smrj 	case DDI_CTLOPS_NREGS:
458*3446Smrj 		if (rdip == (dev_info_t *)0)
459*3446Smrj 			return (DDI_FAILURE);
460*3446Smrj 		break;
461*3446Smrj 
462*3446Smrj 	case DDI_CTLOPS_PEEK:
463*3446Smrj 	case DDI_CTLOPS_POKE:
464*3446Smrj 		ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(dip));
465*3446Smrj 		if (strcmp(ddi_driver_name(ddi_get_parent(dip)), "npe") != 0)
466*3446Smrj 			return (ddi_ctlops(dip, rdip, ctlop, arg, result));
467*3446Smrj 		return (pci_peekpoke_check(dip, rdip, ctlop, arg, result,
468*3446Smrj 		    ddi_ctlops, &ppb->ppb_err_mutex,
469*3446Smrj 		    &ppb->ppb_peek_poke_mutex));
470*3446Smrj 
471*3446Smrj 	default:
472*3446Smrj 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
473*3446Smrj 	}
474*3446Smrj 
475*3446Smrj 	*(int *)result = 0;
476*3446Smrj 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip,
477*3446Smrj 		DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg",
478*3446Smrj 		(caddr_t)&drv_regp, &reglen) != DDI_SUCCESS)
479*3446Smrj 		return (DDI_FAILURE);
480*3446Smrj 
481*3446Smrj 	totreg = reglen / sizeof (pci_regspec_t);
482*3446Smrj 	if (ctlop == DDI_CTLOPS_NREGS)
483*3446Smrj 		*(int *)result = totreg;
484*3446Smrj 	else if (ctlop == DDI_CTLOPS_REGSIZE) {
485*3446Smrj 		rn = *(int *)arg;
486*3446Smrj 		if (rn >= totreg) {
487*3446Smrj 			kmem_free(drv_regp, reglen);
488*3446Smrj 			return (DDI_FAILURE);
489*3446Smrj 		}
490*3446Smrj 		*(off_t *)result = drv_regp[rn].pci_size_low;
491*3446Smrj 	}
492*3446Smrj 
493*3446Smrj 	kmem_free(drv_regp, reglen);
494*3446Smrj 	return (DDI_SUCCESS);
495*3446Smrj }
496*3446Smrj 
497*3446Smrj static int
498*3446Smrj ppb_name_child(dev_info_t *child, char *name, int namelen)
499*3446Smrj {
500*3446Smrj 	pci_regspec_t *pci_rp;
501*3446Smrj 	uint_t slot, func;
502*3446Smrj 	char **unit_addr;
503*3446Smrj 	uint_t n;
504*3446Smrj 
505*3446Smrj 	/*
506*3446Smrj 	 * For .conf nodes, use unit-address property as name
507*3446Smrj 	 */
508*3446Smrj 	if (ndi_dev_is_persistent_node(child) == 0) {
509*3446Smrj 		if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
510*3446Smrj 		    DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) !=
511*3446Smrj 		    DDI_PROP_SUCCESS) {
512*3446Smrj 			cmn_err(CE_WARN,
513*3446Smrj 			    "cannot find unit-address in %s.conf",
514*3446Smrj 			    ddi_driver_name(child));
515*3446Smrj 			return (DDI_FAILURE);
516*3446Smrj 		}
517*3446Smrj 		if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
518*3446Smrj 			cmn_err(CE_WARN, "unit-address property in %s.conf"
519*3446Smrj 			    " not well-formed", ddi_driver_name(child));
520*3446Smrj 			ddi_prop_free(unit_addr);
521*3446Smrj 			return (DDI_SUCCESS);
522*3446Smrj 		}
523*3446Smrj 		(void) snprintf(name, namelen, "%s", *unit_addr);
524*3446Smrj 		ddi_prop_free(unit_addr);
525*3446Smrj 		return (DDI_SUCCESS);
526*3446Smrj 	}
527*3446Smrj 
528*3446Smrj 	/* get child "reg" property */
529*3446Smrj 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
530*3446Smrj 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) {
531*3446Smrj 		return (DDI_FAILURE);
532*3446Smrj 	}
533*3446Smrj 
534*3446Smrj 	/* copy the device identifications */
535*3446Smrj 	slot = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
536*3446Smrj 	func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
537*3446Smrj 
538*3446Smrj 	if (func != 0)
539*3446Smrj 		(void) snprintf(name, namelen, "%x,%x", slot, func);
540*3446Smrj 	else
541*3446Smrj 		(void) snprintf(name, namelen, "%x", slot);
542*3446Smrj 
543*3446Smrj 	ddi_prop_free(pci_rp);
544*3446Smrj 	return (DDI_SUCCESS);
545*3446Smrj }
546*3446Smrj 
547*3446Smrj static int
548*3446Smrj ppb_initchild(dev_info_t *child)
549*3446Smrj {
550*3446Smrj 	struct ddi_parent_private_data *pdptr;
551*3446Smrj 	char name[MAXNAMELEN];
552*3446Smrj 	ddi_acc_handle_t config_handle;
553*3446Smrj 	ushort_t command_preserve, command;
554*3446Smrj 
555*3446Smrj 	if (ppb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS)
556*3446Smrj 		return (DDI_FAILURE);
557*3446Smrj 	ddi_set_name_addr(child, name);
558*3446Smrj 
559*3446Smrj 	/*
560*3446Smrj 	 * Pseudo nodes indicate a prototype node with per-instance
561*3446Smrj 	 * properties to be merged into the real h/w device node.
562*3446Smrj 	 * The interpretation of the unit-address is DD[,F]
563*3446Smrj 	 * where DD is the device id and F is the function.
564*3446Smrj 	 */
565*3446Smrj 	if (ndi_dev_is_persistent_node(child) == 0) {
566*3446Smrj 		extern int pci_allow_pseudo_children;
567*3446Smrj 
568*3446Smrj 		ddi_set_parent_data(child, NULL);
569*3446Smrj 
570*3446Smrj 		/*
571*3446Smrj 		 * Try to merge the properties from this prototype
572*3446Smrj 		 * node into real h/w nodes.
573*3446Smrj 		 */
574*3446Smrj 		if (ndi_merge_node(child, ppb_name_child) == DDI_SUCCESS) {
575*3446Smrj 			/*
576*3446Smrj 			 * Merged ok - return failure to remove the node.
577*3446Smrj 			 */
578*3446Smrj 			ddi_set_name_addr(child, NULL);
579*3446Smrj 			return (DDI_FAILURE);
580*3446Smrj 		}
581*3446Smrj 
582*3446Smrj 		/* workaround for ddivs to run under PCI */
583*3446Smrj 		if (pci_allow_pseudo_children)
584*3446Smrj 			return (DDI_SUCCESS);
585*3446Smrj 
586*3446Smrj 		/*
587*3446Smrj 		 * The child was not merged into a h/w node,
588*3446Smrj 		 * but there's not much we can do with it other
589*3446Smrj 		 * than return failure to cause the node to be removed.
590*3446Smrj 		 */
591*3446Smrj 		cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
592*3446Smrj 		    ddi_driver_name(child), ddi_get_name_addr(child),
593*3446Smrj 		    ddi_driver_name(child));
594*3446Smrj 		ddi_set_name_addr(child, NULL);
595*3446Smrj 		return (DDI_NOT_WELL_FORMED);
596*3446Smrj 	}
597*3446Smrj 
598*3446Smrj 	/* transfer select properties from PROM to kernel */
599*3446Smrj 	if (ddi_getprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS, "interrupts",
600*3446Smrj 		-1) != -1) {
601*3446Smrj 		pdptr = kmem_zalloc((sizeof (struct ddi_parent_private_data) +
602*3446Smrj 		    sizeof (struct intrspec)), KM_SLEEP);
603*3446Smrj 		pdptr->par_intr = (struct intrspec *)(pdptr + 1);
604*3446Smrj 		pdptr->par_nintr = 1;
605*3446Smrj 		ddi_set_parent_data(child, pdptr);
606*3446Smrj 	} else
607*3446Smrj 		ddi_set_parent_data(child, NULL);
608*3446Smrj 
609*3446Smrj 	if (pci_config_setup(child, &config_handle) != DDI_SUCCESS)
610*3446Smrj 		return (DDI_FAILURE);
611*3446Smrj 
612*3446Smrj 	/*
613*3446Smrj 	 * Support for the "command-preserve" property.
614*3446Smrj 	 */
615*3446Smrj 	command_preserve = ddi_prop_get_int(DDI_DEV_T_ANY, child,
616*3446Smrj 	    DDI_PROP_DONTPASS, "command-preserve", 0);
617*3446Smrj 	command = pci_config_get16(config_handle, PCI_CONF_COMM);
618*3446Smrj 	command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB);
619*3446Smrj 	command |= (ppb_command_default & ~command_preserve);
620*3446Smrj 	pci_config_put16(config_handle, PCI_CONF_COMM, command);
621*3446Smrj 
622*3446Smrj 	pci_config_teardown(&config_handle);
623*3446Smrj 	return (DDI_SUCCESS);
624*3446Smrj }
625*3446Smrj 
626*3446Smrj static void
627*3446Smrj ppb_removechild(dev_info_t *dip)
628*3446Smrj {
629*3446Smrj 	struct ddi_parent_private_data *pdptr;
630*3446Smrj 
631*3446Smrj 	if ((pdptr = ddi_get_parent_data(dip)) != NULL) {
632*3446Smrj 		kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec)));
633*3446Smrj 		ddi_set_parent_data(dip, NULL);
634*3446Smrj 	}
635*3446Smrj 	ddi_set_name_addr(dip, NULL);
636*3446Smrj 
637*3446Smrj 	/*
638*3446Smrj 	 * Strip the node to properly convert it back to prototype form
639*3446Smrj 	 */
640*3446Smrj 	ddi_remove_minor_node(dip, NULL);
641*3446Smrj 
642*3446Smrj 	impl_rem_dev_props(dip);
643*3446Smrj }
644*3446Smrj 
645*3446Smrj /*
646*3446Smrj  * ppb_save_config_regs
647*3446Smrj  *
648*3446Smrj  * This routine saves the state of the configuration registers of all
649*3446Smrj  * the child nodes of each PBM.
650*3446Smrj  *
651*3446Smrj  * used by: ppb_detach() on suspends
652*3446Smrj  *
653*3446Smrj  * return value: none
654*3446Smrj  */
655*3446Smrj static void
656*3446Smrj ppb_save_config_regs(ppb_devstate_t *ppb_p)
657*3446Smrj {
658*3446Smrj 	int i;
659*3446Smrj 	dev_info_t *dip;
660*3446Smrj 	ddi_acc_handle_t config_handle;
661*3446Smrj 
662*3446Smrj 	for (i = 0, dip = ddi_get_child(ppb_p->dip); dip != NULL;
663*3446Smrj 			i++, dip = ddi_get_next_sibling(dip)) {
664*3446Smrj 
665*3446Smrj 		if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
666*3446Smrj 			cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
667*3446Smrj 				ddi_driver_name(ppb_p->dip),
668*3446Smrj 				ddi_get_instance(ppb_p->dip),
669*3446Smrj 				ddi_driver_name(dip),
670*3446Smrj 				ddi_get_instance(dip));
671*3446Smrj 			continue;
672*3446Smrj 		}
673*3446Smrj 
674*3446Smrj 		ppb_p->config_state[i].dip = dip;
675*3446Smrj 		ppb_p->config_state[i].command =
676*3446Smrj 			pci_config_get16(config_handle, PCI_CONF_COMM);
677*3446Smrj 		pci_config_teardown(&config_handle);
678*3446Smrj 	}
679*3446Smrj 	ppb_p->config_state_index = i;
680*3446Smrj }
681*3446Smrj 
682*3446Smrj 
683*3446Smrj /*
684*3446Smrj  * ppb_restore_config_regs
685*3446Smrj  *
686*3446Smrj  * This routine restores the state of the configuration registers of all
687*3446Smrj  * the child nodes of each PBM.
688*3446Smrj  *
689*3446Smrj  * used by: ppb_attach() on resume
690*3446Smrj  *
691*3446Smrj  * return value: none
692*3446Smrj  */
693*3446Smrj static void
694*3446Smrj ppb_restore_config_regs(ppb_devstate_t *ppb_p)
695*3446Smrj {
696*3446Smrj 	int i;
697*3446Smrj 	dev_info_t *dip;
698*3446Smrj 	ddi_acc_handle_t config_handle;
699*3446Smrj 
700*3446Smrj 	for (i = 0; i < ppb_p->config_state_index; i++) {
701*3446Smrj 		dip = ppb_p->config_state[i].dip;
702*3446Smrj 		if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
703*3446Smrj 			cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
704*3446Smrj 				ddi_driver_name(ppb_p->dip),
705*3446Smrj 				ddi_get_instance(ppb_p->dip),
706*3446Smrj 				ddi_driver_name(dip),
707*3446Smrj 				ddi_get_instance(dip));
708*3446Smrj 			continue;
709*3446Smrj 		}
710*3446Smrj 		pci_config_put16(config_handle, PCI_CONF_COMM,
711*3446Smrj 				ppb_p->config_state[i].command);
712*3446Smrj 		pci_config_teardown(&config_handle);
713*3446Smrj 	}
714*3446Smrj }
715*3446Smrj 
716*3446Smrj 
717*3446Smrj /*
718*3446Smrj  * returns the location of a hypertransport capability whose upper 16-bit
719*3446Smrj  * register of the cap header matches <reg_val> after masking the register
720*3446Smrj  * with <reg_mask>; if both <reg_mask> and <reg_val> are 0, it will return the
721*3446Smrj  * first HT cap found
722*3446Smrj  */
723*3446Smrj static uint8_t
724*3446Smrj ppb_find_ht_cap(ddi_acc_handle_t cfg_hdl, uint16_t reg_mask, uint16_t reg_val)
725*3446Smrj {
726*3446Smrj 	uint16_t status, reg;
727*3446Smrj 	uint8_t ptr, id;
728*3446Smrj 
729*3446Smrj 	status = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
730*3446Smrj 	if (status == 0xffff || !((status & PCI_STAT_CAP)))
731*3446Smrj 		return (PCI_CAP_NEXT_PTR_NULL);
732*3446Smrj 
733*3446Smrj 	ptr = pci_config_get8(cfg_hdl, PCI_CONF_CAP_PTR);
734*3446Smrj 	while (ptr != 0xFF &&
735*3446Smrj 	    ptr != PCI_CAP_NEXT_PTR_NULL &&
736*3446Smrj 	    ptr >= PCI_CAP_PTR_OFF) {
737*3446Smrj 
738*3446Smrj 		ptr &= PCI_CAP_PTR_MASK;
739*3446Smrj 		id = pci_config_get8(cfg_hdl, ptr + PCI_CAP_ID);
740*3446Smrj 
741*3446Smrj 		if (id == PCI_CAP_ID_HT) {
742*3446Smrj 			reg = pci_config_get16(cfg_hdl,
743*3446Smrj 			    ptr + PCI_CAP_ID_REGS_OFF);
744*3446Smrj 			if ((reg & reg_mask) == reg_val)
745*3446Smrj 				return (ptr);
746*3446Smrj 		}
747*3446Smrj 		ptr = pci_config_get8(cfg_hdl, ptr + PCI_CAP_NEXT_PTR);
748*3446Smrj 	}
749*3446Smrj 
750*3446Smrj 	return (PCI_CAP_NEXT_PTR_NULL);
751*3446Smrj }
752*3446Smrj 
753*3446Smrj 
754*3446Smrj static boolean_t
755*3446Smrj ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl)
756*3446Smrj {
757*3446Smrj 	uint8_t ptr;
758*3446Smrj 
759*3446Smrj 	ptr = ppb_find_ht_cap(cfg_hdl,
760*3446Smrj 	    PCI_CAP_HT_MSIMAP_TYPE_MASK | PCI_CAP_HT_MSIMAP_ENABLE_MASK,
761*3446Smrj 	    PCI_CAP_HT_MSIMAP_TYPE | PCI_CAP_HT_MSIMAP_ENABLE);
762*3446Smrj 
763*3446Smrj 	if (ptr == PCI_CAP_NEXT_PTR_NULL)
764*3446Smrj 		return (B_FALSE);
765*3446Smrj 
766*3446Smrj 	return (B_TRUE);
767*3446Smrj }
768*3446Smrj 
769*3446Smrj 
770*3446Smrj static int
771*3446Smrj ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd)
772*3446Smrj {
773*3446Smrj 	uint8_t ptr;
774*3446Smrj 	uint16_t reg;
775*3446Smrj 
776*3446Smrj 	ptr = ppb_find_ht_cap(cfg_hdl, PCI_CAP_HT_MSIMAP_TYPE_MASK,
777*3446Smrj 	    PCI_CAP_HT_MSIMAP_TYPE);
778*3446Smrj 	if (ptr == PCI_CAP_NEXT_PTR_NULL)
779*3446Smrj 		return (0);
780*3446Smrj 
781*3446Smrj 	reg = pci_config_get16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF);
782*3446Smrj 	switch (cmd) {
783*3446Smrj 	case HT_MSIMAP_ENABLE:
784*3446Smrj 		reg |= PCI_CAP_HT_MSIMAP_ENABLE;
785*3446Smrj 		break;
786*3446Smrj 	case HT_MSIMAP_DISABLE:
787*3446Smrj 	default:
788*3446Smrj 		reg &= ~(uint16_t)PCI_CAP_HT_MSIMAP_ENABLE;
789*3446Smrj 	}
790*3446Smrj 
791*3446Smrj 	pci_config_put16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF, reg);
792*3446Smrj 	return (1);
793*3446Smrj }
794*3446Smrj 
795*3446Smrj 
796*3446Smrj /*
797*3446Smrj  * intercept certain interrupt services to handle special cases
798*3446Smrj  */
799*3446Smrj static int
800*3446Smrj ppb_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
801*3446Smrj     ddi_intr_handle_impl_t *hdlp, void *result)
802*3446Smrj {
803*3446Smrj 	ddi_acc_handle_t cfg_hdl;
804*3446Smrj 	int rv = DDI_SUCCESS;
805*3446Smrj 
806*3446Smrj 	if (intr_op != DDI_INTROP_SUPPORTED_TYPES)
807*3446Smrj 		return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result));
808*3446Smrj 
809*3446Smrj 	DDI_INTR_NEXDBG((CE_CONT,
810*3446Smrj 	    "ppb_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
811*3446Smrj 	    (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
812*3446Smrj 
813*3446Smrj 	/* Fixed interrupt is supported by default */
814*3446Smrj 	*(int *)result = DDI_INTR_TYPE_FIXED;
815*3446Smrj 
816*3446Smrj 	if (ppb_support_msi == -1) {
817*3446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
818*3446Smrj 		    "ppb_intr_ops: MSI is not allowed\n"));
819*3446Smrj 		goto OUT;
820*3446Smrj 	}
821*3446Smrj 
822*3446Smrj 	if (ppb_support_msi == 1) {
823*3446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
824*3446Smrj 		    "ppb_intr_ops: MSI is always allowed\n"));
825*3446Smrj 		rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result);
826*3446Smrj 		goto OUT;
827*3446Smrj 	}
828*3446Smrj 
829*3446Smrj 	if (pci_config_setup(pdip, &cfg_hdl) != DDI_SUCCESS) {
830*3446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
831*3446Smrj 		    "ppb_intr_ops: pci_config_setup() failed\n"));
832*3446Smrj 		goto OUT;
833*3446Smrj 	}
834*3446Smrj 
835*3446Smrj 	/*
836*3446Smrj 	 * check for hypertransport msi mapping capability
837*3446Smrj 	 */
838*3446Smrj 	if (ppb_ht_msimap_check(cfg_hdl)) {
839*3446Smrj 		DDI_INTR_NEXDBG((CE_CONT,
840*3446Smrj 		    "ppb_intr_ops: HT MSI mapping enabled\n"));
841*3446Smrj 		rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result);
842*3446Smrj 	}
843*3446Smrj 
844*3446Smrj 	/*
845*3446Smrj 	 * if we add failure conditions after pci_config_setup, move this to
846*3446Smrj 	 * OUT and use an extra flag to indicate the need to teardown cfg_hdl
847*3446Smrj 	 */
848*3446Smrj 	pci_config_teardown(&cfg_hdl);
849*3446Smrj 
850*3446Smrj OUT:
851*3446Smrj 	DDI_INTR_NEXDBG((CE_CONT,
852*3446Smrj 	    "ppb_intr_ops: rdip 0x%p, returns supported types: 0x%x\n",
853*3446Smrj 	    (void *)rdip, *(int *)result));
854*3446Smrj 	return (rv);
855*3446Smrj }
856*3446Smrj 
857*3446Smrj static int
858*3446Smrj ppb_open(dev_t *devp, int flags, int otyp, cred_t *credp)
859*3446Smrj {
860*3446Smrj 	return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp));
861*3446Smrj }
862*3446Smrj 
863*3446Smrj static int
864*3446Smrj ppb_close(dev_t dev, int flags, int otyp, cred_t *credp)
865*3446Smrj {
866*3446Smrj 	return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp));
867*3446Smrj }
868*3446Smrj 
869*3446Smrj static int
870*3446Smrj ppb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
871*3446Smrj {
872*3446Smrj 	return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, credp,
873*3446Smrj 	    rvalp));
874*3446Smrj }
875*3446Smrj 
876*3446Smrj static int
877*3446Smrj ppb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
878*3446Smrj 	int flags, char *name, caddr_t valuep, int *lengthp)
879*3446Smrj {
880*3446Smrj 	return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags,
881*3446Smrj 	    name, valuep, lengthp));
882*3446Smrj }
883*3446Smrj 
884*3446Smrj static int
885*3446Smrj ppb_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
886*3446Smrj {
887*3446Smrj 	return (pcihp_info(dip, cmd, arg, result));
888*3446Smrj }
889*3446Smrj 
890*3446Smrj /*ARGSUSED*/
891*3446Smrj static int
892*3446Smrj ppb_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap,
893*3446Smrj     ddi_iblock_cookie_t *ibc)
894*3446Smrj {
895*3446Smrj 	ppb_devstate_t  *ppb = ddi_get_soft_state(ppb_state,
896*3446Smrj 	    ddi_get_instance(dip));
897*3446Smrj 
898*3446Smrj 	ASSERT(ibc != NULL);
899*3446Smrj 	*ibc = ppb->ppb_fm_ibc;
900*3446Smrj 
901*3446Smrj 	return (ppb->ppb_fmcap);
902*3446Smrj }
903*3446Smrj 
904*3446Smrj /*ARGSUSED*/
905*3446Smrj static int
906*3446Smrj ppb_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *no_used)
907*3446Smrj {
908*3446Smrj 	ppb_devstate_t  *ppb = ddi_get_soft_state(ppb_state,
909*3446Smrj 	    ddi_get_instance(dip));
910*3446Smrj 
911*3446Smrj 	mutex_enter(&ppb->ppb_err_mutex);
912*3446Smrj 	pci_ereport_post(dip, derr, NULL);
913*3446Smrj 	mutex_exit(&ppb->ppb_err_mutex);
914*3446Smrj 	return (derr->fme_status);
915*3446Smrj }
916