xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/pcibus/pcibus.c (revision ed5289f91b9bf164dccd6c75398362be77a4478d)
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 2008 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 #include <sys/fm/protocol.h>
30 #include <assert.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <alloca.h>
36 #include <sys/param.h>
37 #include <sys/pci.h>
38 #include <sys/pcie.h>
39 #include <libdevinfo.h>
40 #include <libnvpair.h>
41 #include <fm/topo_mod.h>
42 #include <fm/topo_hc.h>
43 
44 #include <hostbridge.h>
45 #include <pcibus.h>
46 #include <did.h>
47 #include <did_props.h>
48 #include <util.h>
49 
50 extern txprop_t Bus_common_props[];
51 extern txprop_t Dev_common_props[];
52 extern txprop_t Fn_common_props[];
53 extern int Bus_propcnt;
54 extern int Dev_propcnt;
55 extern int Fn_propcnt;
56 
57 extern int platform_pci_label(topo_mod_t *mod, tnode_t *, nvlist_t *,
58     nvlist_t **);
59 extern int platform_pci_fru(topo_mod_t *mod, tnode_t *, nvlist_t *,
60     nvlist_t **);
61 static void pci_release(topo_mod_t *, tnode_t *);
62 static int pci_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
63     topo_instance_t, void *, void *);
64 static int pci_label(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
65     nvlist_t **);
66 static int pci_fru(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
67     nvlist_t **);
68 
69 static const topo_modops_t Pci_ops =
70 	{ pci_enum, pci_release };
71 static const topo_modinfo_t Pci_info =
72 	{ PCI_BUS, FM_FMRI_SCHEME_HC, PCI_ENUMR_VERS, &Pci_ops };
73 
74 static const topo_method_t Pci_methods[] = {
75 	{ TOPO_METH_LABEL, TOPO_METH_LABEL_DESC,
76 	    TOPO_METH_LABEL_VERSION, TOPO_STABILITY_INTERNAL, pci_label },
77 	{ TOPO_METH_FRU_COMPUTE, TOPO_METH_FRU_COMPUTE_DESC,
78 	    TOPO_METH_FRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL, pci_fru },
79 	{ NULL }
80 };
81 
82 int
83 _topo_init(topo_mod_t *modhdl, topo_version_t version)
84 {
85 	/*
86 	 * Turn on module debugging output
87 	 */
88 	if (getenv("TOPOPCIDBG") != NULL)
89 		topo_mod_setdebug(modhdl);
90 	topo_mod_dprintf(modhdl, "initializing pcibus builtin\n");
91 
92 	if (version != PCI_ENUMR_VERS)
93 		return (topo_mod_seterrno(modhdl, EMOD_VER_NEW));
94 
95 	topo_mod_register(modhdl, &Pci_info, TOPO_VERSION);
96 	topo_mod_dprintf(modhdl, "PCI Enumr initd\n");
97 
98 	return (0);
99 }
100 
101 void
102 _topo_fini(topo_mod_t *modhdl)
103 {
104 	topo_mod_unregister(modhdl);
105 }
106 
107 static int
108 pci_label(topo_mod_t *mp, tnode_t *node, topo_version_t version,
109     nvlist_t *in, nvlist_t **out)
110 {
111 	if (version > TOPO_METH_LABEL_VERSION)
112 		return (topo_mod_seterrno(mp, EMOD_VER_NEW));
113 	return (platform_pci_label(mp, node, in, out));
114 }
115 static int
116 pci_fru(topo_mod_t *mp, tnode_t *node, topo_version_t version,
117     nvlist_t *in, nvlist_t **out)
118 {
119 	if (version > TOPO_METH_FRU_COMPUTE_VERSION)
120 		return (topo_mod_seterrno(mp, EMOD_VER_NEW));
121 	return (platform_pci_fru(mp, node, in, out));
122 }
123 static tnode_t *
124 pci_tnode_create(topo_mod_t *mod, tnode_t *parent,
125     const char *name, topo_instance_t i, void *priv)
126 {
127 	tnode_t *ntn;
128 
129 	if ((ntn = tnode_create(mod, parent, name, i, priv)) == NULL)
130 		return (NULL);
131 	if (topo_method_register(mod, ntn, Pci_methods) < 0) {
132 		topo_mod_dprintf(mod, "topo_method_register failed: %s\n",
133 		    topo_strerror(topo_mod_errno(mod)));
134 		topo_node_unbind(ntn);
135 		return (NULL);
136 	}
137 	return (ntn);
138 }
139 
140 /*ARGSUSED*/
141 static int
142 hostbridge_asdevice(topo_mod_t *mod, tnode_t *bus)
143 {
144 	di_node_t di;
145 	tnode_t *dev32;
146 
147 	di = topo_node_getspecific(bus);
148 	assert(di != DI_NODE_NIL);
149 
150 	if ((dev32 = pcidev_declare(mod, bus, di, 32)) == NULL)
151 		return (-1);
152 	if (pcifn_declare(mod, dev32, di, 0) == NULL) {
153 		topo_node_unbind(dev32);
154 		return (-1);
155 	}
156 	return (0);
157 }
158 
159 tnode_t *
160 pciexfn_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
161     topo_instance_t i)
162 {
163 	did_t *pd;
164 	tnode_t *ntn;
165 
166 	if ((pd = did_find(mod, dn)) == NULL)
167 		return (NULL);
168 	if ((ntn = pci_tnode_create(mod, parent, PCIEX_FUNCTION, i, dn))
169 	    == NULL)
170 		return (NULL);
171 	if (did_props_set(ntn, pd, Fn_common_props, Fn_propcnt) < 0) {
172 		topo_node_unbind(ntn);
173 		return (NULL);
174 	}
175 	/*
176 	 * We may find pci-express buses or plain-pci buses beneath a function
177 	 */
178 	if (child_range_add(mod, ntn, PCIEX_BUS, 0, MAX_HB_BUSES) < 0) {
179 		topo_node_unbind(ntn);
180 		return (NULL);
181 	}
182 	if (child_range_add(mod, ntn, PCI_BUS, 0, MAX_HB_BUSES) < 0) {
183 		topo_node_range_destroy(ntn, PCIEX_BUS);
184 		topo_node_unbind(ntn);
185 		return (NULL);
186 	}
187 	return (ntn);
188 }
189 
190 tnode_t *
191 pciexdev_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
192     topo_instance_t i)
193 {
194 	did_t *pd;
195 	tnode_t *ntn;
196 
197 	if ((pd = did_find(mod, dn)) == NULL)
198 		return (NULL);
199 	did_settnode(pd, parent);
200 
201 	if ((ntn = pci_tnode_create(mod, parent, PCIEX_DEVICE, i, dn)) == NULL)
202 		return (NULL);
203 	if (did_props_set(ntn, pd, Dev_common_props, Dev_propcnt) < 0) {
204 		topo_node_unbind(ntn);
205 		return (NULL);
206 	}
207 
208 	/*
209 	 * We can expect to find pci-express functions beneath the device
210 	 */
211 	if (child_range_add(mod,
212 	    ntn, PCIEX_FUNCTION, 0, MAX_PCIDEV_FNS) < 0) {
213 		topo_node_unbind(ntn);
214 		return (NULL);
215 	}
216 	return (ntn);
217 }
218 
219 tnode_t *
220 pciexbus_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
221     topo_instance_t i)
222 {
223 	did_t *pd;
224 	tnode_t *ntn;
225 
226 	if ((pd = did_find(mod, dn)) == NULL)
227 		return (NULL);
228 	did_settnode(pd, parent);
229 	if ((ntn = pci_tnode_create(mod, parent, PCIEX_BUS, i, dn)) == NULL)
230 		return (NULL);
231 	if (did_props_set(ntn, pd, Bus_common_props, Bus_propcnt) < 0) {
232 		topo_node_unbind(ntn);
233 		return (NULL);
234 	}
235 	/*
236 	 * We can expect to find pci-express devices beneath the bus
237 	 */
238 	if (child_range_add(mod,
239 	    ntn, PCIEX_DEVICE, 0, MAX_PCIBUS_DEVS) < 0) {
240 		topo_node_unbind(ntn);
241 		return (NULL);
242 	}
243 	return (ntn);
244 }
245 
246 tnode_t *
247 pcifn_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
248     topo_instance_t i)
249 {
250 	did_t *pd;
251 	tnode_t *ntn;
252 
253 	if ((pd = did_find(mod, dn)) == NULL)
254 		return (NULL);
255 	if ((ntn = pci_tnode_create(mod, parent, PCI_FUNCTION, i, dn)) == NULL)
256 		return (NULL);
257 	if (did_props_set(ntn, pd, Fn_common_props, Fn_propcnt) < 0) {
258 		topo_node_unbind(ntn);
259 		return (NULL);
260 	}
261 	/*
262 	 * We may find pci buses beneath a function
263 	 */
264 	if (child_range_add(mod, ntn, PCI_BUS, 0, MAX_HB_BUSES) < 0) {
265 		topo_node_unbind(ntn);
266 		return (NULL);
267 	}
268 	return (ntn);
269 }
270 
271 tnode_t *
272 pcidev_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
273     topo_instance_t i)
274 {
275 	did_t *pd;
276 	tnode_t *ntn;
277 
278 	if ((pd = did_find(mod, dn)) == NULL)
279 		return (NULL);
280 	/* remember parent tnode */
281 	did_settnode(pd, parent);
282 
283 	if ((ntn = pci_tnode_create(mod, parent, PCI_DEVICE, i, dn)) == NULL)
284 		return (NULL);
285 	if (did_props_set(ntn, pd, Dev_common_props, Dev_propcnt) < 0) {
286 		topo_node_unbind(ntn);
287 		return (NULL);
288 	}
289 
290 	/*
291 	 * We can expect to find pci functions beneath the device
292 	 */
293 	if (child_range_add(mod, ntn, PCI_FUNCTION, 0, MAX_PCIDEV_FNS) < 0) {
294 		topo_node_unbind(ntn);
295 		return (NULL);
296 	}
297 	return (ntn);
298 }
299 
300 tnode_t *
301 pcibus_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
302     topo_instance_t i)
303 {
304 	did_t *pd;
305 	tnode_t *ntn;
306 	int hbchild = 0;
307 
308 	if ((pd = did_find(mod, dn)) == NULL)
309 		return (NULL);
310 	did_settnode(pd, parent);
311 	if ((ntn = pci_tnode_create(mod, parent, PCI_BUS, i, dn)) == NULL)
312 		return (NULL);
313 	/*
314 	 * If our devinfo node is lacking certain information of its
315 	 * own, and our parent topology node is a hostbridge, we may
316 	 * need/want to inherit information available in the
317 	 * hostbridge node's private data.
318 	 */
319 	if (strcmp(topo_node_name(parent), HOSTBRIDGE) == 0)
320 		hbchild = 1;
321 	if (did_props_set(ntn, pd, Bus_common_props, Bus_propcnt) < 0) {
322 		topo_node_unbind(ntn);
323 		return (NULL);
324 	}
325 	/*
326 	 * We can expect to find pci devices beneath the bus
327 	 */
328 	if (child_range_add(mod, ntn, PCI_DEVICE, 0, MAX_PCIBUS_DEVS) < 0) {
329 		topo_node_unbind(ntn);
330 		return (NULL);
331 	}
332 	/*
333 	 * On each bus child of the hostbridge, we represent the
334 	 * hostbridge as a device outside the range of legal device
335 	 * numbers.
336 	 */
337 	if (hbchild == 1) {
338 		if (hostbridge_asdevice(mod, ntn) < 0) {
339 			topo_node_range_destroy(ntn, PCI_DEVICE);
340 			topo_node_unbind(ntn);
341 			return (NULL);
342 		}
343 	}
344 	return (ntn);
345 }
346 
347 static int
348 pci_bridge_declare(topo_mod_t *mod, tnode_t *fn, di_node_t din, int board,
349     int bridge, int rc, int depth)
350 {
351 	int err, excap, extyp;
352 
353 	excap = pciex_cap_get(mod, din);
354 	extyp = excap & PCIE_PCIECAP_DEV_TYPE_MASK;
355 	if (excap <= 0 ||
356 	    extyp != PCIE_PCIECAP_DEV_TYPE_PCIE2PCI)
357 		err = pci_children_instantiate(mod, fn, din, board, bridge,
358 		    rc, TRUST_BDF, depth + 1);
359 	else
360 		err = pci_children_instantiate(mod, fn, din, board, bridge,
361 		    rc - TO_PCI, TRUST_BDF, depth + 1);
362 	return (err);
363 }
364 
365 static void
366 declare_dev_and_fn(topo_mod_t *mod, tnode_t *bus, tnode_t **dev, di_node_t din,
367     int board, int bridge, int rc, int devno, int fnno, int depth)
368 {
369 	int dcnt = 0;
370 	tnode_t *fn;
371 	uint_t class, subclass;
372 	uint_t vid, did;
373 	did_t *dp = NULL;
374 
375 	if (*dev == NULL) {
376 		if (rc >= 0)
377 			*dev = pciexdev_declare(mod, bus, din, devno);
378 		else
379 			*dev = pcidev_declare(mod, bus, din, devno);
380 		if (*dev == NULL)
381 			return;
382 		++dcnt;
383 	}
384 	if (rc >= 0)
385 		fn = pciexfn_declare(mod, *dev, din, fnno);
386 	else
387 		fn = pcifn_declare(mod, *dev, din, fnno);
388 
389 	if (fn == NULL) {
390 		if (dcnt) {
391 			topo_node_unbind(*dev);
392 			*dev = NULL;
393 		}
394 		return;
395 	}
396 
397 	if (pci_classcode_get(mod, din, &class, &subclass) < 0) {
398 		topo_node_unbind(fn);
399 		if (dcnt)
400 			topo_node_unbind(*dev);
401 		return;
402 	}
403 
404 	/*
405 	 * This function may be a bridge.  If not, check for a possible
406 	 * topology map file and kick off its enumeration of lower-level
407 	 * devices.
408 	 */
409 	if (class == PCI_CLASS_BRIDGE && subclass == PCI_BRIDGE_PCI) {
410 		(void) pci_bridge_declare(mod, fn, din, board, bridge, rc,
411 		    depth);
412 	}
413 
414 	/*
415 	 * Check for a Neptune-based NIC. This could either be a Neptune
416 	 * adapter card or an Neptune ASIC on a board (e.g. motherboard)
417 	 *
418 	 * For Netpune adapter cards, use xfp-hc-topology.xml to expand
419 	 * topology to include the XFP optical module, which is a FRU on
420 	 * the Neptune based 10giga fiber NICs.
421 	 *
422 	 * For Neptune ASICs, use the XAUI enumerator to expand topology.
423 	 * The 10giga ports are externalized by a XAUI cards, which
424 	 * are FRUs. The XAUI enumerator in turn instantiates the XFP
425 	 * optical module FRUs.
426 	 */
427 	else if (class == PCI_CLASS_NET &&
428 	    di_uintprop_get(mod, din, DI_VENDIDPROP, &vid) >= 0 &&
429 	    di_uintprop_get(mod, din, DI_DEVIDPROP, &did) >= 0) {
430 		if (vid == SUN_VENDOR_ID && did == NEPTUNE_DEVICE_ID) {
431 			/*
432 			 * Is this an adapter card? Check the bus's physlot
433 			 */
434 			dp = did_find(mod, topo_node_getspecific(bus));
435 			if (did_physslot(dp) >= 0) {
436 				topo_mod_dprintf(mod, "Found Neptune slot\n");
437 				(void) topo_mod_enummap(mod, fn,
438 				    "xfp", FM_FMRI_SCHEME_HC);
439 			} else {
440 				topo_mod_dprintf(mod, "Found Neptune ASIC\n");
441 				if (topo_mod_load(mod, XAUI, TOPO_VERSION) ==
442 				    NULL) {
443 					topo_mod_dprintf(mod, "pcibus enum "
444 					    "could not load xaui enum\n");
445 					topo_mod_seterrno(mod,
446 					    EMOD_PARTIAL_ENUM);
447 					return;
448 				} else {
449 					if (topo_node_range_create(mod, fn,
450 					    XAUI, 0, 1) < 0) {
451 						topo_mod_dprintf(mod,
452 						    "child_range_add for "
453 						    "XAUI failed: %s\n",
454 						    topo_strerror(
455 						    topo_mod_errno(mod)));
456 						return;
457 					}
458 					(void) topo_mod_enumerate(mod, fn,
459 					    XAUI, XAUI, fnno, fnno, fn);
460 				}
461 			}
462 		}
463 	}
464 }
465 
466 int
467 pci_children_instantiate(topo_mod_t *mod, tnode_t *parent, di_node_t pn,
468     int board, int bridge, int rc, int bover, int depth)
469 {
470 	did_t *pps[MAX_PCIBUS_DEVS][MAX_PCIDEV_FNS];
471 	did_t *bp = NULL;
472 	did_t *np;
473 	di_node_t sib;
474 	di_node_t din;
475 	tnode_t *bn = NULL;
476 	tnode_t *dn = NULL;
477 	int pb = -1;
478 	int b, d, f;
479 
480 	for (d = 0; d < MAX_PCIBUS_DEVS; d++)
481 		for (f = 0; f < MAX_PCIDEV_FNS; f++)
482 			pps[d][f] = NULL;
483 
484 	/* start at the parent's first sibling */
485 	sib = di_child_node(pn);
486 	while (sib != DI_NODE_NIL) {
487 		np = did_create(mod, sib, board, bridge, rc, bover);
488 		if (np == NULL)
489 			return (-1);
490 		did_BDF(np, &b, &d, &f);
491 		pps[d][f] = np;
492 		if (bp == NULL)
493 			bp = np;
494 		if (pb < 0)
495 			pb = ((bover == TRUST_BDF) ? b : bover);
496 		sib = di_sibling_node(sib);
497 	}
498 	if (pb < 0 && bover < 0)
499 		return (0);
500 	if (rc >= 0)
501 		bn = pciexbus_declare(mod, parent, pn, ((pb < 0) ? bover : pb));
502 	else
503 		bn = pcibus_declare(mod, parent, pn, ((pb < 0) ? bover : pb));
504 	if (bn == NULL)
505 		return (-1);
506 	if (pb < 0)
507 		return (0);
508 
509 	for (d = 0; d < MAX_PCIBUS_DEVS; d++) {
510 		for (f = 0; f < MAX_PCIDEV_FNS; f++) {
511 			if (pps[d][f] == NULL)
512 				continue;
513 			din = did_dinode(pps[d][f]);
514 
515 			/*
516 			 * Try to enumerate as many devices and functions as
517 			 * possible.  If we fail to declare a device, break
518 			 * out of the function loop.
519 			 */
520 			declare_dev_and_fn(mod, bn,
521 			    &dn, din, board, bridge, rc, d, f, depth);
522 			did_rele(pps[d][f]);
523 
524 			if (dn == NULL)
525 				break;
526 		}
527 		dn = NULL;
528 	}
529 	return (0);
530 }
531 
532 static int
533 pciexbus_enum(topo_mod_t *mp, tnode_t *ptn, char *pnm, topo_instance_t min,
534     topo_instance_t max)
535 {
536 	di_node_t pdn;
537 	int rc;
538 	int retval;
539 
540 	/*
541 	 * PCI-Express; root complex shares the hostbridge's instance
542 	 * number.  Parent node's private data is a simple di_node_t
543 	 * and we have to construct our own did hash and did_t.
544 	 */
545 	rc = topo_node_instance(ptn);
546 
547 	if ((pdn = topo_node_getspecific(ptn)) == DI_NODE_NIL) {
548 		topo_mod_dprintf(mp,
549 		    "Parent %s node missing private data.\n"
550 		    "Unable to proceed with %s enumeration.\n", pnm, PCIEX_BUS);
551 		return (0);
552 	}
553 	if (did_hash_init(mp) != 0)
554 		return (-1);
555 	if ((did_create(mp, pdn, 0, 0, rc, TRUST_BDF)) == NULL)
556 		return (-1);	/* errno already set */
557 
558 	retval = pci_children_instantiate(mp, ptn, pdn, 0, 0, rc,
559 	    (min == max) ? min : TRUST_BDF, 0);
560 	did_hash_fini(mp);
561 
562 	return (retval);
563 }
564 
565 static int
566 pcibus_enum(topo_mod_t *mp, tnode_t *ptn, char *pnm, topo_instance_t min,
567     topo_instance_t max, void *data)
568 {
569 	did_t *didp, *hbdid = (did_t *)data;
570 	int retval;
571 
572 	/*
573 	 * XXTOPO: we should not be sharing private node data with another
574 	 * module. PCI Bus; Parent node's private data is a did_t.  We'll
575 	 * use the did hash established by the parent.
576 	 */
577 	did_setspecific(mp, data);
578 
579 	/*
580 	 * If we're looking for a specific bus-instance, find the right
581 	 * did_t in the chain, otherwise, there should be only one did_t.
582 	 */
583 	if (min == max) {
584 		int b;
585 		didp = hbdid;
586 		while (didp != NULL) {
587 			did_BDF(didp, &b, NULL, NULL);
588 			if (b == min)
589 				break;
590 			didp = did_link_get(didp);
591 		}
592 		if (didp == NULL) {
593 			topo_mod_dprintf(mp,
594 			    "Parent %s node missing private data related\n"
595 			    "to %s instance %d.\n", pnm, PCI_BUS, min);
596 			topo_mod_setspecific(mp, NULL);
597 			return (0);
598 		}
599 	} else {
600 		assert(did_link_get(hbdid) == NULL);
601 		didp = hbdid;
602 	}
603 	retval = pci_children_instantiate(mp, ptn, did_dinode(didp),
604 	    did_board(didp), did_bridge(didp), did_rc(didp),
605 	    (min == max) ? min : TRUST_BDF, 0);
606 
607 	topo_mod_setspecific(mp, NULL);
608 
609 	return (retval);
610 }
611 
612 /*ARGSUSED*/
613 static int
614 pci_enum(topo_mod_t *mod, tnode_t *ptn, const char *name,
615     topo_instance_t min, topo_instance_t max, void *notused, void *data)
616 {
617 	int retval;
618 	char *pname;
619 
620 	topo_mod_dprintf(mod, "Enumerating pci!\n");
621 
622 	if (strcmp(name, PCI_BUS) != 0 && strcmp(name, PCIEX_BUS) != 0) {
623 		topo_mod_dprintf(mod,
624 		    "Currently only know how to enumerate %s or %s.\n",
625 		    PCI_BUS, PCIEX_BUS);
626 		return (0);
627 	}
628 	pname = topo_node_name(ptn);
629 	if (strcmp(pname, HOSTBRIDGE) != 0 && strcmp(pname, PCIEX_ROOT) != 0) {
630 		topo_mod_dprintf(mod,
631 		    "Currently can only enumerate a %s or %s directly\n",
632 		    PCI_BUS, PCIEX_BUS);
633 		topo_mod_dprintf(mod,
634 		    "descended from a %s or %s node.\n",
635 		    HOSTBRIDGE, PCIEX_ROOT);
636 		return (0);
637 	}
638 
639 	if (strcmp(name, PCI_BUS) == 0) {
640 		retval = pcibus_enum(mod, ptn, pname, min, max, data);
641 	} else if (strcmp(name, PCIEX_BUS) == 0) {
642 		retval = pciexbus_enum(mod, ptn, pname, min, max);
643 	} else {
644 		topo_mod_dprintf(mod,
645 		    "Currently only know how to enumerate %s or %s not %s.\n",
646 		    PCI_BUS, PCIEX_BUS, name);
647 		return (0);
648 	}
649 
650 	return (retval);
651 }
652 
653 /*ARGSUSED*/
654 static void
655 pci_release(topo_mod_t *mp, tnode_t *node)
656 {
657 	topo_method_unregister_all(mp, node);
658 
659 	/*
660 	 * node private data (did_t) for this node is destroyed in
661 	 * did_hash_destroy()
662 	 */
663 
664 	topo_node_unbind(node);
665 }
666