xref: /titanic_51/usr/src/uts/i86pc/io/pciex/npe.c (revision f8d8d7b32d22897bd2af32f5fa5bbdc9c0797277)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  *	Host to PCI-Express local bus driver
31  */
32 
33 #include <sys/conf.h>
34 #include <sys/modctl.h>
35 #include <sys/pcie.h>
36 #include <sys/pci_impl.h>
37 #include <sys/sysmacros.h>
38 #include <sys/ddi_intr.h>
39 #include <sys/sunndi.h>
40 #include <sys/sunddi.h>
41 #include <sys/ddifm.h>
42 #include <sys/ndifm.h>
43 #include <sys/fm/util.h>
44 #include <sys/hotplug/pci/pcihp.h>
45 #include <io/pci/pci_common.h>
46 #include <io/pci/pci_tools_ext.h>
47 #include <io/pciex/pcie_error.h>
48 
49 /*
50  * Bus Operation functions
51  */
52 static int	npe_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *,
53 		    off_t, off_t, caddr_t *);
54 static int	npe_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
55 		    void *, void *);
56 static int	npe_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t,
57 		    ddi_intr_handle_impl_t *, void *);
58 static int	npe_fm_init(dev_info_t *, dev_info_t *, int,
59 		    ddi_iblock_cookie_t *);
60 
61 static int	npe_fm_callback(dev_info_t *, ddi_fm_error_t *, const void *);
62 
63 struct bus_ops npe_bus_ops = {
64 	BUSO_REV,
65 	npe_bus_map,
66 	NULL,
67 	NULL,
68 	NULL,
69 	i_ddi_map_fault,
70 	ddi_dma_map,
71 	ddi_dma_allochdl,
72 	ddi_dma_freehdl,
73 	ddi_dma_bindhdl,
74 	ddi_dma_unbindhdl,
75 	ddi_dma_flush,
76 	ddi_dma_win,
77 	ddi_dma_mctl,
78 	npe_ctlops,
79 	ddi_bus_prop_op,
80 	0,		/* (*bus_get_eventcookie)();	*/
81 	0,		/* (*bus_add_eventcall)();	*/
82 	0,		/* (*bus_remove_eventcall)();	*/
83 	0,		/* (*bus_post_event)();		*/
84 	0,		/* (*bus_intr_ctl)(); */
85 	0,		/* (*bus_config)(); */
86 	0,		/* (*bus_unconfig)(); */
87 	npe_fm_init,	/* (*bus_fm_init)(); */
88 	NULL,		/* (*bus_fm_fini)(); */
89 	NULL,		/* (*bus_fm_access_enter)(); */
90 	NULL,		/* (*bus_fm_access_exit)(); */
91 	NULL,		/* (*bus_power)(); */
92 	npe_intr_ops	/* (*bus_intr_op)(); */
93 };
94 
95 /*
96  * One goal here is to leverage off of the pcihp.c source without making
97  * changes to it.  Call into it's cb_ops directly if needed, piggybacking
98  * anything else needed by the pci_tools.c module.  Only pci_tools and pcihp
99  * will be using the PCI devctl node.
100  */
101 static int	npe_open(dev_t *, int, int, cred_t *);
102 static int	npe_close(dev_t, int, int, cred_t *);
103 static int	npe_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
104 static int	npe_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
105 		    caddr_t, int *);
106 static int	npe_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
107 
108 struct cb_ops npe_cb_ops = {
109 	npe_open,			/* open */
110 	npe_close,			/* close */
111 	nodev,				/* strategy */
112 	nodev,				/* print */
113 	nodev,				/* dump */
114 	nodev,				/* read */
115 	nodev,				/* write */
116 	npe_ioctl,			/* ioctl */
117 	nodev,				/* devmap */
118 	nodev,				/* mmap */
119 	nodev,				/* segmap */
120 	nochpoll,			/* poll */
121 	npe_prop_op,			/* cb_prop_op */
122 	NULL,				/* streamtab */
123 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
124 	CB_REV,				/* rev */
125 	nodev,				/* int (*cb_aread)() */
126 	nodev				/* int (*cb_awrite)() */
127 };
128 
129 
130 /*
131  * Device Node Operation functions
132  */
133 static int	npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
134 static int	npe_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
135 
136 struct dev_ops npe_ops = {
137 	DEVO_REV,		/* devo_rev */
138 	0,			/* refcnt  */
139 	npe_info,		/* info */
140 	nulldev,		/* identify */
141 	nulldev,		/* probe */
142 	npe_attach,		/* attach */
143 	npe_detach,		/* detach */
144 	nulldev,		/* reset */
145 	&npe_cb_ops,		/* driver operations */
146 	&npe_bus_ops		/* bus operations */
147 };
148 
149 /*
150  * Internal routines in support of particular npe_ctlops.
151  */
152 static int npe_removechild(dev_info_t *child);
153 static int npe_initchild(dev_info_t *child);
154 
155 /*
156  * External support routine
157  */
158 extern void	npe_query_acpi_mcfg(dev_info_t *dip);
159 extern void	npe_ck804_fix_aer_ptr(dev_info_t *child);
160 
161 /*
162  * Module linkage information for the kernel.
163  */
164 static struct modldrv modldrv = {
165 	&mod_driverops, /* Type of module */
166 	"Host to PCIe nexus driver %I%",
167 	&npe_ops,	/* driver ops */
168 };
169 
170 static struct modlinkage modlinkage = {
171 	MODREV_1,
172 	(void *)&modldrv,
173 	NULL
174 };
175 
176 /* Save minimal state. */
177 void *npe_statep;
178 
179 
180 int
181 _init(void)
182 {
183 	int e;
184 
185 	/*
186 	 * Initialize per-pci bus soft state pointer.
187 	 */
188 	e = ddi_soft_state_init(&npe_statep, sizeof (pci_state_t), 1);
189 	if (e != 0)
190 		return (e);
191 
192 	if ((e = mod_install(&modlinkage)) != 0)
193 		ddi_soft_state_fini(&npe_statep);
194 
195 	return (e);
196 }
197 
198 
199 int
200 _fini(void)
201 {
202 	int rc;
203 
204 	rc = mod_remove(&modlinkage);
205 	if (rc != 0)
206 		return (rc);
207 
208 	ddi_soft_state_fini(&npe_statep);
209 	return (rc);
210 }
211 
212 
213 int
214 _info(struct modinfo *modinfop)
215 {
216 	return (mod_info(&modlinkage, modinfop));
217 }
218 
219 /*ARGSUSED*/
220 static int
221 npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
222 {
223 	/*
224 	 * Use the minor number as constructed by pcihp, as the index value to
225 	 * ddi_soft_state_zalloc.
226 	 */
227 	int instance = ddi_get_instance(devi);
228 	pci_state_t *pcip = NULL;
229 
230 	if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, "device_type",
231 	    "pciex") != DDI_PROP_SUCCESS) {
232 		cmn_err(CE_WARN, "npe:  'device_type' prop create failed");
233 	}
234 
235 	if (ddi_soft_state_zalloc(npe_statep, instance) == DDI_SUCCESS)
236 		pcip = ddi_get_soft_state(npe_statep, instance);
237 
238 	if (pcip == NULL)
239 		return (DDI_FAILURE);
240 
241 	pcip->pci_dip = devi;
242 
243 	/*
244 	 * Initialize hotplug support on this bus. At minimum
245 	 * (for non hotplug bus) this would create ":devctl" minor
246 	 * node to support DEVCTL_DEVICE_* and DEVCTL_BUS_* ioctls
247 	 * to this bus.
248 	 */
249 	if (pcihp_init(devi) != DDI_SUCCESS) {
250 		cmn_err(CE_WARN, "npe: Failed to setup hotplug framework");
251 		ddi_soft_state_free(npe_statep, instance);
252 		return (DDI_FAILURE);
253 	}
254 
255 	/* Second arg: initialize for pci_express root nexus */
256 	if (pcitool_init(devi, B_TRUE) != DDI_SUCCESS) {
257 		(void) pcihp_uninit(devi);
258 		ddi_soft_state_free(npe_statep, instance);
259 		return (DDI_FAILURE);
260 	}
261 	pcip->pci_fmcap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE |
262 	    DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
263 	ddi_fm_init(devi, &pcip->pci_fmcap, &pcip->pci_fm_ibc);
264 
265 	if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE)
266 		ddi_fm_handler_register(devi, npe_fm_callback, NULL);
267 
268 	npe_query_acpi_mcfg(devi);
269 	ddi_report_dev(devi);
270 	return (DDI_SUCCESS);
271 
272 }
273 
274 
275 /*ARGSUSED*/
276 static int
277 npe_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
278 {
279 	int instance = ddi_get_instance(devi);
280 	pci_state_t *pcip;
281 
282 	pcip = ddi_get_soft_state(npe_statep, ddi_get_instance(devi));
283 
284 	/* Uninitialize pcitool support. */
285 	pcitool_uninit(devi);
286 
287 	/*
288 	 * Uninitialize hotplug support on this bus.
289 	 */
290 	(void) pcihp_uninit(devi);
291 
292 	if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE)
293 		ddi_fm_handler_unregister(devi);
294 
295 	ddi_fm_fini(devi);
296 	ddi_soft_state_free(npe_statep, instance);
297 	return (DDI_SUCCESS);
298 }
299 
300 
301 static int
302 npe_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
303     off_t offset, off_t len, caddr_t *vaddrp)
304 {
305 	int 		rnumber;
306 	int		length;
307 	int		space;
308 	ddi_acc_impl_t	*ap;
309 	ddi_acc_hdl_t	*hp;
310 	ddi_map_req_t	mr;
311 	pci_regspec_t	pci_reg;
312 	pci_regspec_t	*pci_rp;
313 	struct regspec	reg;
314 	pci_acc_cfblk_t	*cfp;
315 	int		retval;
316 
317 	mr = *mp; /* Get private copy of request */
318 	mp = &mr;
319 
320 	/*
321 	 * check for register number
322 	 */
323 	switch (mp->map_type) {
324 	case DDI_MT_REGSPEC:
325 		pci_reg = *(pci_regspec_t *)(mp->map_obj.rp);
326 		pci_rp = &pci_reg;
327 		if (pci_common_get_reg_prop(rdip, pci_rp) != DDI_SUCCESS)
328 			return (DDI_FAILURE);
329 		break;
330 	case DDI_MT_RNUMBER:
331 		rnumber = mp->map_obj.rnumber;
332 		/*
333 		 * get ALL "reg" properties for dip, select the one of
334 		 * of interest. In x86, "assigned-addresses" property
335 		 * is identical to the "reg" property, so there is no
336 		 * need to cross check the two to determine the physical
337 		 * address of the registers.
338 		 * This routine still performs some validity checks to
339 		 * make sure that everything is okay.
340 		 */
341 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
342 		    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
343 		    (uint_t *)&length) != DDI_PROP_SUCCESS)
344 			return (DDI_FAILURE);
345 
346 		/*
347 		 * validate the register number.
348 		 */
349 		length /= (sizeof (pci_regspec_t) / sizeof (int));
350 		if (rnumber >= length) {
351 			ddi_prop_free(pci_rp);
352 			return (DDI_FAILURE);
353 		}
354 
355 		/*
356 		 * copy the required entry.
357 		 */
358 		pci_reg = pci_rp[rnumber];
359 
360 		/*
361 		 * free the memory allocated by ddi_prop_lookup_int_array
362 		 */
363 		ddi_prop_free(pci_rp);
364 
365 		pci_rp = &pci_reg;
366 		if (pci_common_get_reg_prop(rdip, pci_rp) != DDI_SUCCESS)
367 			return (DDI_FAILURE);
368 		mp->map_type = DDI_MT_REGSPEC;
369 		break;
370 	default:
371 		return (DDI_ME_INVAL);
372 	}
373 
374 	space = pci_rp->pci_phys_hi & PCI_REG_ADDR_M;
375 
376 	/*
377 	 * check for unmap and unlock of address space
378 	 */
379 	if ((mp->map_op == DDI_MO_UNMAP) || (mp->map_op == DDI_MO_UNLOCK)) {
380 		switch (space) {
381 		case PCI_ADDR_IO:
382 			reg.regspec_bustype = 1;
383 			break;
384 
385 		case PCI_ADDR_CONFIG:
386 			/*
387 			 * Check for AMD's northbridges
388 			 *	AND
389 			 * for any PCI device.
390 			 *
391 			 * This is a workaround fix for
392 			 * AMD-8132's inability to handle MMCFG
393 			 * accesses on Galaxy's PE servers
394 			 *	AND
395 			 * to disable MMCFG for any PCI device.
396 			 *
397 			 * If a device is *not* found to have PCIe
398 			 * capability, then assume it is a PCI device.
399 			 */
400 
401 			if (is_amd_northbridge(rdip) == 0 ||
402 			    (ddi_prop_get_int(DDI_DEV_T_ANY, rdip,
403 			    DDI_PROP_DONTPASS, "pcie-capid-pointer",
404 			    PCI_CAP_NEXT_PTR_NULL) == PCI_CAP_NEXT_PTR_NULL)) {
405 				if (DDI_FM_ACC_ERR_CAP(ddi_fm_capable(rdip)) &&
406 				    mp->map_handlep->ah_acc.devacc_attr_access
407 				    != DDI_DEFAULT_ACC) {
408 					ndi_fmc_remove(rdip, ACC_HANDLE,
409 					    (void *)mp->map_handlep);
410 				}
411 				return (DDI_SUCCESS);
412 			}
413 
414 
415 			/* FALLTHROUGH */
416 		case PCI_ADDR_MEM64:
417 			/*
418 			 * MEM64 requires special treatment on map, to check
419 			 * that the device is below 4G.  On unmap, however,
420 			 * we can assume that everything is OK... the map
421 			 * must have succeeded.
422 			 */
423 			/* FALLTHROUGH */
424 		case PCI_ADDR_MEM32:
425 			reg.regspec_bustype = 0;
426 			break;
427 
428 		default:
429 			return (DDI_FAILURE);
430 		}
431 
432 		/*
433 		 * Adjust offset and length
434 		 * A non-zero length means override the one in the regspec.
435 		 */
436 		pci_rp->pci_phys_low += (uint_t)offset;
437 		if (len != 0)
438 			pci_rp->pci_size_low = len;
439 
440 		reg.regspec_addr = pci_rp->pci_phys_low;
441 		reg.regspec_size = pci_rp->pci_size_low;
442 
443 		mp->map_obj.rp = &reg;
444 		retval = ddi_map(dip, mp, (off_t)0, (off_t)0, vaddrp);
445 		if (DDI_FM_ACC_ERR_CAP(ddi_fm_capable(rdip)) &&
446 		    mp->map_handlep->ah_acc.devacc_attr_access !=
447 		    DDI_DEFAULT_ACC) {
448 			ndi_fmc_remove(rdip, ACC_HANDLE,
449 			    (void *)mp->map_handlep);
450 		}
451 		return (retval);
452 
453 	}
454 
455 	/* check for user mapping request - not legal for Config */
456 	if (mp->map_op == DDI_MO_MAP_HANDLE && space == PCI_ADDR_CONFIG) {
457 		cmn_err(CE_NOTE, "npe: Config mapping request from user\n");
458 		return (DDI_FAILURE);
459 	}
460 
461 
462 	/*
463 	 * Note that pci_fm_acc_setup() is called to serve two purposes
464 	 * i) enable legacy PCI I/O style config space access
465 	 * ii) register with FMA
466 	 */
467 	if (space == PCI_ADDR_CONFIG) {
468 		/* Can't map config space without a handle */
469 		hp = (ddi_acc_hdl_t *)mp->map_handlep;
470 		if (hp == NULL)
471 			return (DDI_FAILURE);
472 
473 		/* record the device address for future reference */
474 		cfp = (pci_acc_cfblk_t *)&hp->ah_bus_private;
475 		cfp->c_busnum = PCI_REG_BUS_G(pci_rp->pci_phys_hi);
476 		cfp->c_devnum = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
477 		cfp->c_funcnum = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
478 
479 		*vaddrp = (caddr_t)offset;
480 
481 		/*
482 		 * Check for AMD's northbridges, pci devices and
483 		 * devices underneath a pci bridge.  This is to setup
484 		 * I/O based config space access.
485 		 */
486 		if (is_amd_northbridge(rdip) == 0 ||
487 		    (ddi_prop_get_int(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
488 		    "pcie-capid-pointer", PCI_CAP_NEXT_PTR_NULL) ==
489 		    PCI_CAP_NEXT_PTR_NULL)) {
490 			int ret;
491 
492 			if ((ret = pci_fm_acc_setup(hp, offset, len)) ==
493 			    DDI_SUCCESS) {
494 				if (DDI_FM_ACC_ERR_CAP(ddi_fm_capable(rdip)) &&
495 				    mp->map_handlep->ah_acc.devacc_attr_access
496 				    != DDI_DEFAULT_ACC) {
497 					ndi_fmc_insert(rdip, ACC_HANDLE,
498 					    (void *)mp->map_handlep, NULL);
499 				}
500 			}
501 			return (ret);
502 		}
503 
504 		pci_rp->pci_phys_low = ddi_prop_get_int64(DDI_DEV_T_ANY,
505 		    rdip, 0, "ecfga-base-address", 0);
506 
507 		pci_rp->pci_phys_low += ((cfp->c_busnum << 20) |
508 		    (cfp->c_devnum) << 15 | (cfp->c_funcnum << 12));
509 
510 		pci_rp->pci_size_low = PCIE_CONF_HDR_SIZE;
511 	}
512 
513 	length = pci_rp->pci_size_low;
514 
515 	/*
516 	 * range check
517 	 */
518 	if ((offset >= length) || (len > length) || (offset + len > length))
519 		return (DDI_FAILURE);
520 
521 	/*
522 	 * Adjust offset and length
523 	 * A non-zero length means override the one in the regspec.
524 	 */
525 	pci_rp->pci_phys_low += (uint_t)offset;
526 	if (len != 0)
527 		pci_rp->pci_size_low = len;
528 
529 	/*
530 	 * convert the pci regsec into the generic regspec used by the
531 	 * parent root nexus driver.
532 	 */
533 	switch (space) {
534 	case PCI_ADDR_IO:
535 		reg.regspec_bustype = 1;
536 		break;
537 	case PCI_ADDR_CONFIG:
538 	case PCI_ADDR_MEM64:
539 		/*
540 		 * We can't handle 64-bit devices that are mapped above
541 		 * 4G or that are larger than 4G.
542 		 */
543 		if (pci_rp->pci_phys_mid != 0 || pci_rp->pci_size_hi != 0)
544 			return (DDI_FAILURE);
545 		/*
546 		 * Other than that, we can treat them as 32-bit mappings
547 		 */
548 		/* FALLTHROUGH */
549 	case PCI_ADDR_MEM32:
550 		reg.regspec_bustype = 0;
551 		break;
552 	default:
553 		return (DDI_FAILURE);
554 	}
555 
556 	reg.regspec_addr = pci_rp->pci_phys_low;
557 	reg.regspec_size = pci_rp->pci_size_low;
558 
559 	mp->map_obj.rp = &reg;
560 	retval = ddi_map(dip, mp, (off_t)0, (off_t)0, vaddrp);
561 	if (retval == DDI_SUCCESS) {
562 		/*
563 		 * For config space gets force use of cautious access routines.
564 		 * These will handle default and protected mode accesses too.
565 		 */
566 		if (space == PCI_ADDR_CONFIG) {
567 			ap = (ddi_acc_impl_t *)mp->map_handlep;
568 			ap->ahi_acc_attr &= ~DDI_ACCATTR_DIRECT;
569 			ap->ahi_acc_attr |= DDI_ACCATTR_CONFIG_SPACE;
570 			ap->ahi_get8 = i_ddi_caut_get8;
571 			ap->ahi_get16 = i_ddi_caut_get16;
572 			ap->ahi_get32 = i_ddi_caut_get32;
573 			ap->ahi_get64 = i_ddi_caut_get64;
574 			ap->ahi_rep_get8 = i_ddi_caut_rep_get8;
575 			ap->ahi_rep_get16 = i_ddi_caut_rep_get16;
576 			ap->ahi_rep_get32 = i_ddi_caut_rep_get32;
577 			ap->ahi_rep_get64 = i_ddi_caut_rep_get64;
578 		}
579 		if (DDI_FM_ACC_ERR_CAP(ddi_fm_capable(rdip)) &&
580 		    mp->map_handlep->ah_acc.devacc_attr_access !=
581 		    DDI_DEFAULT_ACC) {
582 			ndi_fmc_insert(rdip, ACC_HANDLE,
583 			    (void *)mp->map_handlep, NULL);
584 		}
585 	}
586 	return (retval);
587 }
588 
589 
590 
591 /*ARGSUSED*/
592 static int
593 npe_ctlops(dev_info_t *dip, dev_info_t *rdip,
594 	ddi_ctl_enum_t ctlop, void *arg, void *result)
595 {
596 	int		rn;
597 	int		totreg;
598 	uint_t		reglen;
599 	pci_regspec_t	*drv_regp;
600 
601 	switch (ctlop) {
602 	case DDI_CTLOPS_REPORTDEV:
603 		if (rdip == (dev_info_t *)0)
604 			return (DDI_FAILURE);
605 		cmn_err(CE_CONT, "?PCI Express-device: %s@%s, %s%d\n",
606 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
607 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
608 		return (DDI_SUCCESS);
609 
610 	case DDI_CTLOPS_INITCHILD:
611 		return (npe_initchild((dev_info_t *)arg));
612 
613 	case DDI_CTLOPS_UNINITCHILD:
614 		return (npe_removechild((dev_info_t *)arg));
615 
616 	case DDI_CTLOPS_SIDDEV:
617 		return (DDI_SUCCESS);
618 
619 	case DDI_CTLOPS_REGSIZE:
620 	case DDI_CTLOPS_NREGS:
621 		if (rdip == (dev_info_t *)0)
622 			return (DDI_FAILURE);
623 
624 		*(int *)result = 0;
625 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
626 		    DDI_PROP_DONTPASS, "reg", (int **)&drv_regp,
627 		    &reglen) != DDI_PROP_SUCCESS) {
628 			return (DDI_FAILURE);
629 		}
630 
631 		totreg = (reglen * sizeof (int)) / sizeof (pci_regspec_t);
632 		if (ctlop == DDI_CTLOPS_NREGS)
633 			*(int *)result = totreg;
634 		else if (ctlop == DDI_CTLOPS_REGSIZE) {
635 			rn = *(int *)arg;
636 			if (rn >= totreg) {
637 				ddi_prop_free(drv_regp);
638 				return (DDI_FAILURE);
639 			}
640 			*(off_t *)result = drv_regp[rn].pci_size_low;
641 		}
642 		ddi_prop_free(drv_regp);
643 
644 		return (DDI_SUCCESS);
645 
646 	case DDI_CTLOPS_POWER:
647 	{
648 		power_req_t	*reqp = (power_req_t *)arg;
649 		/*
650 		 * We currently understand reporting of PCI_PM_IDLESPEED
651 		 * capability. Everything else is passed up.
652 		 */
653 		if ((reqp->request_type == PMR_REPORT_PMCAP) &&
654 		    (reqp->req.report_pmcap_req.cap ==  PCI_PM_IDLESPEED))
655 			return (DDI_SUCCESS);
656 
657 		break;
658 	}
659 
660 	case DDI_CTLOPS_PEEK:
661 	case DDI_CTLOPS_POKE:
662 		return (pci_common_peekpoke(dip, rdip, ctlop, arg, result));
663 
664 	default:
665 		break;
666 	}
667 
668 	return (ddi_ctlops(dip, rdip, ctlop, arg, result));
669 
670 }
671 
672 
673 /*
674  * npe_intr_ops
675  */
676 static int
677 npe_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
678     ddi_intr_handle_impl_t *hdlp, void *result)
679 {
680 	return (pci_common_intr_ops(pdip, rdip, intr_op, hdlp, result));
681 }
682 
683 
684 static int
685 npe_initchild(dev_info_t *child)
686 {
687 	char	name[80];
688 
689 	if (pci_common_name_child(child, name, 80) != DDI_SUCCESS)
690 		return (DDI_FAILURE);
691 
692 	ddi_set_name_addr(child, name);
693 
694 	/*
695 	 * Pseudo nodes indicate a prototype node with per-instance
696 	 * properties to be merged into the real h/w device node.
697 	 * The interpretation of the unit-address is DD[,F]
698 	 * where DD is the device id and F is the function.
699 	 */
700 	if (ndi_dev_is_persistent_node(child) == 0) {
701 		extern int pci_allow_pseudo_children;
702 
703 		ddi_set_parent_data(child, NULL);
704 
705 		/*
706 		 * Try to merge the properties from this prototype
707 		 * node into real h/w nodes.
708 		 */
709 		if (ndi_merge_node(child, pci_common_name_child) ==
710 		    DDI_SUCCESS) {
711 			/*
712 			 * Merged ok - return failure to remove the node.
713 			 */
714 			ddi_set_name_addr(child, NULL);
715 			return (DDI_FAILURE);
716 		}
717 
718 		/* workaround for DDIVS to run under PCI Express */
719 		if (pci_allow_pseudo_children) {
720 			/*
721 			 * If the "interrupts" property doesn't exist,
722 			 * this must be the ddivs no-intr case, and it returns
723 			 * DDI_SUCCESS instead of DDI_FAILURE.
724 			 */
725 			if (ddi_prop_get_int(DDI_DEV_T_ANY, child,
726 			    DDI_PROP_DONTPASS, "interrupts", -1) == -1)
727 				return (DDI_SUCCESS);
728 			/*
729 			 * Create the ddi_parent_private_data for a pseudo
730 			 * child.
731 			 */
732 			pci_common_set_parent_private_data(child);
733 			return (DDI_SUCCESS);
734 		}
735 
736 		/*
737 		 * The child was not merged into a h/w node,
738 		 * but there's not much we can do with it other
739 		 * than return failure to cause the node to be removed.
740 		 */
741 		cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
742 		    ddi_get_name(child), ddi_get_name_addr(child),
743 		    ddi_get_name(child));
744 		ddi_set_name_addr(child, NULL);
745 		return (DDI_NOT_WELL_FORMED);
746 	}
747 
748 	if (ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
749 	    "interrupts", -1) != -1)
750 		pci_common_set_parent_private_data(child);
751 	else
752 		ddi_set_parent_data(child, NULL);
753 
754 	/*
755 	 * Enable AER next pointer being displayed
756 	 */
757 	npe_ck804_fix_aer_ptr(child);
758 
759 	(void) pcie_error_init(child);
760 
761 	return (DDI_SUCCESS);
762 }
763 
764 
765 static int
766 npe_removechild(dev_info_t *dip)
767 {
768 	struct ddi_parent_private_data *pdptr;
769 
770 	/*
771 	 * Do it way early.
772 	 * Otherwise ddi_map() call form pcie_error_fini crashes
773 	 */
774 	pcie_error_fini(dip);
775 
776 	if ((pdptr = ddi_get_parent_data(dip)) != NULL) {
777 		kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec)));
778 		ddi_set_parent_data(dip, NULL);
779 	}
780 	ddi_set_name_addr(dip, NULL);
781 
782 	/*
783 	 * Strip the node to properly convert it back to prototype form
784 	 */
785 	ddi_remove_minor_node(dip, NULL);
786 
787 	ddi_prop_remove_all(dip);
788 
789 	return (DDI_SUCCESS);
790 }
791 
792 
793 /*
794  * When retrofitting this module for pci_tools, functions such as open, close,
795  * and ioctl are now pulled into this module.  Before this, the functions in
796  * the pcihp module were referenced directly.  Now they are called or
797  * referenced through the pcihp cb_ops structure from functions in this module.
798  */
799 static int
800 npe_open(dev_t *devp, int flags, int otyp, cred_t *credp)
801 {
802 	return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp));
803 }
804 
805 static int
806 npe_close(dev_t dev, int flags, int otyp, cred_t *credp)
807 {
808 	return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp));
809 }
810 
811 static int
812 npe_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
813 {
814 	minor_t		minor = getminor(dev);
815 	int		instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
816 	pci_state_t	*pci_p = ddi_get_soft_state(npe_statep, instance);
817 	dev_info_t	*dip;
818 
819 	if (pci_p == NULL)
820 		return (ENXIO);
821 
822 	dip = pci_p->pci_dip;
823 	return (pci_common_ioctl(dip, dev, cmd, arg, mode, credp, rvalp));
824 }
825 
826 static int
827 npe_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
828 	int flags, char *name, caddr_t valuep, int *lengthp)
829 {
830 	return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags,
831 	    name, valuep, lengthp));
832 }
833 
834 static int
835 npe_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
836 {
837 	return (pcihp_info(dip, cmd, arg, result));
838 }
839 
840 /*ARGSUSED*/
841 static int
842 npe_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap,
843     ddi_iblock_cookie_t *ibc)
844 {
845 	pci_state_t  *pcip = ddi_get_soft_state(npe_statep,
846 	    ddi_get_instance(dip));
847 
848 	ASSERT(ibc != NULL);
849 	*ibc = pcip->pci_fm_ibc;
850 
851 	return (pcip->pci_fmcap);
852 }
853 
854 /*ARGSUSED*/
855 static int
856 npe_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *no_used)
857 {
858 	return (ndi_fm_handler_dispatch(dip, NULL, derr));
859 }
860