view usr/src/lib/fm/topo/modules/common/pcibus/pcibus.c @ 11050:be69f645ce17

6894028 Some fm libraries are not lint clean 6897387 IPMI facility provider methods not registered on some platforms 6893713 Some fm cmds are not lint clean
author Robert Johnston <Robert.Johnston@Sun.COM>
date Wed, 11 Nov 2009 18:52:48 -0800
parents 0c3d84a756da
children bcacc803343d
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/fm/protocol.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <alloca.h>
#include <sys/param.h>
#include <sys/pci.h>
#include <sys/pcie.h>
#include <libdevinfo.h>
#include <libnvpair.h>
#include <fm/topo_mod.h>
#include <fm/topo_hc.h>

#include <hostbridge.h>
#include <pcibus.h>
#include <did.h>
#include <did_props.h>
#include <util.h>

extern txprop_t Bus_common_props[];
extern txprop_t Dev_common_props[];
extern txprop_t Fn_common_props[];
extern int Bus_propcnt;
extern int Dev_propcnt;
extern int Fn_propcnt;

extern int platform_pci_label(topo_mod_t *mod, tnode_t *, nvlist_t *,
    nvlist_t **);
extern int platform_pci_fru(topo_mod_t *mod, tnode_t *, nvlist_t *,
    nvlist_t **);
static void pci_release(topo_mod_t *, tnode_t *);
static int pci_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
    topo_instance_t, void *, void *);
static int pci_label(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
    nvlist_t **);
static int pci_fru(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
    nvlist_t **);

static const topo_modops_t Pci_ops =
	{ pci_enum, pci_release };
static const topo_modinfo_t Pci_info =
	{ PCI_BUS, FM_FMRI_SCHEME_HC, PCI_ENUMR_VERS, &Pci_ops };

static const topo_method_t Pci_methods[] = {
	{ TOPO_METH_LABEL, TOPO_METH_LABEL_DESC,
	    TOPO_METH_LABEL_VERSION, TOPO_STABILITY_INTERNAL, pci_label },
	{ TOPO_METH_FRU_COMPUTE, TOPO_METH_FRU_COMPUTE_DESC,
	    TOPO_METH_FRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL, pci_fru },
	{ NULL }
};

int
_topo_init(topo_mod_t *modhdl, topo_version_t version)
{
	/*
	 * Turn on module debugging output
	 */
	if (getenv("TOPOPCIDBG") != NULL)
		topo_mod_setdebug(modhdl);
	topo_mod_dprintf(modhdl, "initializing pcibus builtin\n");

	if (version != PCI_ENUMR_VERS)
		return (topo_mod_seterrno(modhdl, EMOD_VER_NEW));

	if (topo_mod_register(modhdl, &Pci_info, TOPO_VERSION) != 0) {
		topo_mod_dprintf(modhdl, "failed to register module");
		return (-1);
	}
	topo_mod_dprintf(modhdl, "PCI Enumr initd\n");

	return (0);
}

void
_topo_fini(topo_mod_t *modhdl)
{
	topo_mod_unregister(modhdl);
}

static int
pci_label(topo_mod_t *mp, tnode_t *node, topo_version_t version,
    nvlist_t *in, nvlist_t **out)
{
	if (version > TOPO_METH_LABEL_VERSION)
		return (topo_mod_seterrno(mp, EMOD_VER_NEW));
	return (platform_pci_label(mp, node, in, out));
}
static int
pci_fru(topo_mod_t *mp, tnode_t *node, topo_version_t version,
    nvlist_t *in, nvlist_t **out)
{
	if (version > TOPO_METH_FRU_COMPUTE_VERSION)
		return (topo_mod_seterrno(mp, EMOD_VER_NEW));
	return (platform_pci_fru(mp, node, in, out));
}
static tnode_t *
pci_tnode_create(topo_mod_t *mod, tnode_t *parent,
    const char *name, topo_instance_t i, void *priv)
{
	tnode_t *ntn;

	if ((ntn = tnode_create(mod, parent, name, i, priv)) == NULL)
		return (NULL);
	if (topo_method_register(mod, ntn, Pci_methods) < 0) {
		topo_mod_dprintf(mod, "topo_method_register failed: %s\n",
		    topo_strerror(topo_mod_errno(mod)));
		topo_node_unbind(ntn);
		return (NULL);
	}
	return (ntn);
}

/*ARGSUSED*/
static int
hostbridge_asdevice(topo_mod_t *mod, tnode_t *bus)
{
	di_node_t di;
	tnode_t *dev32;

	di = topo_node_getspecific(bus);
	assert(di != DI_NODE_NIL);

	if ((dev32 = pcidev_declare(mod, bus, di, 32)) == NULL)
		return (-1);
	if (pcifn_declare(mod, dev32, di, 0) == NULL) {
		topo_node_unbind(dev32);
		return (-1);
	}
	return (0);
}

tnode_t *
pciexfn_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
    topo_instance_t i)
{
	did_t *pd;
	tnode_t *ntn, *ptn;
	di_node_t pdn;
	uint_t class, subclass;
	char *devtyp, *pdevtyp;
	int pcie_devtyp, pexcap;
	boolean_t dev_is_pcie, pdev_is_pcie;

	/* We need the parent's dev info node for some of the info */
	ptn = find_predecessor(parent, PCIEX_FUNCTION);
	/* If this is the first child under root, get root's ptn */
	if (ptn == NULL)
		ptn = find_predecessor(parent, PCIEX_ROOT);
	if (ptn == NULL)
		return (NULL);
	pdn = topo_node_getspecific(ptn);

	/* Get the required info to populate the excap */
	(void) pci_classcode_get(mod, dn, &class, &subclass);
	devtyp = pci_devtype_get(mod, dn);
	pdevtyp = pci_devtype_get(mod, pdn);
	pexcap = pciex_cap_get(mod, pdn);

	dev_is_pcie = devtyp && (strcmp(devtyp, "pciex") == 0);
	pdev_is_pcie = pdevtyp && (strcmp(pdevtyp, "pciex") == 0);

	/*
	 * Populate the excap with correct PCIe device type.
	 *
	 * Device	Parent		Device		Parent	Device
	 * excap	device-type	device-type	excap	Class Code
	 * -------------------------------------------------------------------
	 * PCI(default)	pci		N/A		N/A	!= bridge
	 * PCIe		pciex		N/A		N/A	!= bridge
	 * Root Port	Defined in hostbridge
	 * Switch Up	pciex		pciex		!= up	= bridge
	 * Switch Down	pciex		pciex		= up	= bridge
	 * PCIe-PCI	pciex		pci		N/A	= bridge
	 * PCI-PCIe	pci		pciex		N/A	= bridge
	 */
	pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_PCI_DEV;
	if (class == PCI_CLASS_BRIDGE && subclass == PCI_BRIDGE_PCI) {
		if (pdev_is_pcie) {
			if (dev_is_pcie) {
				if (pexcap != PCIE_PCIECAP_DEV_TYPE_UP)
					pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_UP;
				else
					pcie_devtyp =
					    PCIE_PCIECAP_DEV_TYPE_DOWN;
			} else {
				pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_PCIE2PCI;
			}
		} else {
			if (dev_is_pcie)
				pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_PCI2PCIE;
		}
	} else {
		if (pdev_is_pcie)
			pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_PCIE_DEV;
	}

	if ((pd = did_find(mod, dn)) == NULL)
		return (NULL);
	did_excap_set(pd, pcie_devtyp);

	if ((ntn = pci_tnode_create(mod, parent, PCIEX_FUNCTION, i, dn))
	    == NULL)
		return (NULL);
	if (did_props_set(ntn, pd, Fn_common_props, Fn_propcnt) < 0) {
		topo_node_unbind(ntn);
		return (NULL);
	}
	/*
	 * We may find pci-express buses or plain-pci buses beneath a function
	 */
	if (child_range_add(mod, ntn, PCIEX_BUS, 0, MAX_HB_BUSES) < 0) {
		topo_node_unbind(ntn);
		return (NULL);
	}
	if (child_range_add(mod, ntn, PCI_BUS, 0, MAX_HB_BUSES) < 0) {
		topo_node_range_destroy(ntn, PCIEX_BUS);
		topo_node_unbind(ntn);
		return (NULL);
	}
	return (ntn);
}

tnode_t *
pciexdev_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
    topo_instance_t i)
{
	did_t *pd;
	tnode_t *ntn;

	if ((pd = did_find(mod, dn)) == NULL)
		return (NULL);
	did_settnode(pd, parent);

	if ((ntn = pci_tnode_create(mod, parent, PCIEX_DEVICE, i, dn)) == NULL)
		return (NULL);
	if (did_props_set(ntn, pd, Dev_common_props, Dev_propcnt) < 0) {
		topo_node_unbind(ntn);
		return (NULL);
	}

	/*
	 * We can expect to find pci-express functions beneath the device
	 */
	if (child_range_add(mod,
	    ntn, PCIEX_FUNCTION, 0, MAX_PCIDEV_FNS) < 0) {
		topo_node_unbind(ntn);
		return (NULL);
	}
	return (ntn);
}

tnode_t *
pciexbus_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
    topo_instance_t i)
{
	did_t *pd;
	tnode_t *ntn;

	if ((pd = did_find(mod, dn)) == NULL)
		return (NULL);
	did_settnode(pd, parent);
	if ((ntn = pci_tnode_create(mod, parent, PCIEX_BUS, i, dn)) == NULL)
		return (NULL);
	if (did_props_set(ntn, pd, Bus_common_props, Bus_propcnt) < 0) {
		topo_node_unbind(ntn);
		return (NULL);
	}
	/*
	 * We can expect to find pci-express devices beneath the bus
	 */
	if (child_range_add(mod,
	    ntn, PCIEX_DEVICE, 0, MAX_PCIBUS_DEVS) < 0) {
		topo_node_unbind(ntn);
		return (NULL);
	}
	return (ntn);
}

tnode_t *
pcifn_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
    topo_instance_t i)
{
	did_t *pd;
	tnode_t *ntn;

	if ((pd = did_find(mod, dn)) == NULL)
		return (NULL);
	did_excap_set(pd, PCIE_PCIECAP_DEV_TYPE_PCI_DEV);

	if ((ntn = pci_tnode_create(mod, parent, PCI_FUNCTION, i, dn)) == NULL)
		return (NULL);
	if (did_props_set(ntn, pd, Fn_common_props, Fn_propcnt) < 0) {
		topo_node_unbind(ntn);
		return (NULL);
	}
	/*
	 * We may find pci buses beneath a function
	 */
	if (child_range_add(mod, ntn, PCI_BUS, 0, MAX_HB_BUSES) < 0) {
		topo_node_unbind(ntn);
		return (NULL);
	}
	return (ntn);
}

tnode_t *
pcidev_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
    topo_instance_t i)
{
	did_t *pd;
	tnode_t *ntn;

	if ((pd = did_find(mod, dn)) == NULL)
		return (NULL);
	/* remember parent tnode */
	did_settnode(pd, parent);

	if ((ntn = pci_tnode_create(mod, parent, PCI_DEVICE, i, dn)) == NULL)
		return (NULL);
	if (did_props_set(ntn, pd, Dev_common_props, Dev_propcnt) < 0) {
		topo_node_unbind(ntn);
		return (NULL);
	}

	/*
	 * We can expect to find pci functions beneath the device
	 */
	if (child_range_add(mod, ntn, PCI_FUNCTION, 0, MAX_PCIDEV_FNS) < 0) {
		topo_node_unbind(ntn);
		return (NULL);
	}
	return (ntn);
}

tnode_t *
pcibus_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
    topo_instance_t i)
{
	did_t *pd;
	tnode_t *ntn;
	int hbchild = 0;

	if ((pd = did_find(mod, dn)) == NULL)
		return (NULL);
	did_settnode(pd, parent);
	if ((ntn = pci_tnode_create(mod, parent, PCI_BUS, i, dn)) == NULL)
		return (NULL);
	/*
	 * If our devinfo node is lacking certain information of its
	 * own, and our parent topology node is a hostbridge, we may
	 * need/want to inherit information available in the
	 * hostbridge node's private data.
	 */
	if (strcmp(topo_node_name(parent), HOSTBRIDGE) == 0)
		hbchild = 1;
	if (did_props_set(ntn, pd, Bus_common_props, Bus_propcnt) < 0) {
		topo_node_unbind(ntn);
		return (NULL);
	}
	/*
	 * We can expect to find pci devices beneath the bus
	 */
	if (child_range_add(mod, ntn, PCI_DEVICE, 0, MAX_PCIBUS_DEVS) < 0) {
		topo_node_unbind(ntn);
		return (NULL);
	}
	/*
	 * On each bus child of the hostbridge, we represent the
	 * hostbridge as a device outside the range of legal device
	 * numbers.
	 */
	if (hbchild == 1) {
		if (hostbridge_asdevice(mod, ntn) < 0) {
			topo_node_range_destroy(ntn, PCI_DEVICE);
			topo_node_unbind(ntn);
			return (NULL);
		}
	}
	return (ntn);
}

static int
pci_bridge_declare(topo_mod_t *mod, tnode_t *fn, di_node_t din, int board,
    int bridge, int rc, int depth)
{
	int err;
	char *devtyp;

	devtyp = pci_devtype_get(mod, din);
	/* Check if the children are PCI or PCIe */
	if (devtyp && (strcmp(devtyp, "pciex") == 0))
		err = pci_children_instantiate(mod, fn, din, board, bridge,
		    rc, TRUST_BDF, depth + 1);
	else
		err = pci_children_instantiate(mod, fn, din, board, bridge,
		    rc - TO_PCI, TRUST_BDF, depth + 1);
	return (err);
}

static void
declare_dev_and_fn(topo_mod_t *mod, tnode_t *bus, tnode_t **dev, di_node_t din,
    int board, int bridge, int rc, int devno, int fnno, int depth)
{
	int dcnt = 0;
	tnode_t *fn;
	uint_t class, subclass;
	uint_t vid, did;
	did_t *dp = NULL;

	if (*dev == NULL) {
		if (rc >= 0)
			*dev = pciexdev_declare(mod, bus, din, devno);
		else
			*dev = pcidev_declare(mod, bus, din, devno);
		if (*dev == NULL)
			return;
		++dcnt;
	}
	if (rc >= 0)
		fn = pciexfn_declare(mod, *dev, din, fnno);
	else
		fn = pcifn_declare(mod, *dev, din, fnno);

	if (fn == NULL) {
		if (dcnt) {
			topo_node_unbind(*dev);
			*dev = NULL;
		}
		return;
	}

	if (pci_classcode_get(mod, din, &class, &subclass) < 0) {
		topo_node_unbind(fn);
		if (dcnt)
			topo_node_unbind(*dev);
		return;
	}

	/*
	 * This function may be a bridge.  If not, check for a possible
	 * topology map file and kick off its enumeration of lower-level
	 * devices.
	 */
	if (class == PCI_CLASS_BRIDGE && subclass == PCI_BRIDGE_PCI) {
		(void) pci_bridge_declare(mod, fn, din, board, bridge, rc,
		    depth);
	}

	/*
	 * Check for a Neptune-based NIC. This could either be a Neptune
	 * adapter card or an Neptune ASIC on a board (e.g. motherboard)
	 *
	 * For Netpune adapter cards, use xfp-hc-topology.xml to expand
	 * topology to include the XFP optical module, which is a FRU on
	 * the Neptune based 10giga fiber NICs.
	 *
	 * For Neptune ASICs, use the XAUI enumerator to expand topology.
	 * The 10giga ports are externalized by a XAUI cards, which
	 * are FRUs. The XAUI enumerator in turn instantiates the XFP
	 * optical module FRUs.
	 */
	else if (class == PCI_CLASS_NET &&
	    di_uintprop_get(mod, din, DI_VENDIDPROP, &vid) >= 0 &&
	    di_uintprop_get(mod, din, DI_DEVIDPROP, &did) >= 0) {
		if (vid == SUN_VENDOR_ID && did == NEPTUNE_DEVICE_ID) {
			/*
			 * Is this an adapter card? Check the bus's physlot
			 */
			dp = did_find(mod, topo_node_getspecific(bus));
			if (did_physlot(dp) >= 0) {
				topo_mod_dprintf(mod, "Found Neptune slot\n");
				(void) topo_mod_enummap(mod, fn,
				    "xfp", FM_FMRI_SCHEME_HC);
			} else {
				topo_mod_dprintf(mod, "Found Neptune ASIC\n");
				if (topo_mod_load(mod, XAUI, TOPO_VERSION) ==
				    NULL) {
					topo_mod_dprintf(mod, "pcibus enum "
					    "could not load xaui enum\n");
					(void) topo_mod_seterrno(mod,
					    EMOD_PARTIAL_ENUM);
					return;
				} else {
					if (topo_node_range_create(mod, fn,
					    XAUI, 0, 1) < 0) {
						topo_mod_dprintf(mod,
						    "child_range_add for "
						    "XAUI failed: %s\n",
						    topo_strerror(
						    topo_mod_errno(mod)));
						return;
					}
					(void) topo_mod_enumerate(mod, fn,
					    XAUI, XAUI, fnno, fnno, fn);
				}
			}
		}
	}
}

int
pci_children_instantiate(topo_mod_t *mod, tnode_t *parent, di_node_t pn,
    int board, int bridge, int rc, int bover, int depth)
{
	did_t *pps[MAX_PCIBUS_DEVS][MAX_PCIDEV_FNS];
	did_t *bp = NULL;
	did_t *np;
	di_node_t sib;
	di_node_t din;
	tnode_t *bn = NULL;
	tnode_t *dn = NULL;
	int pb = -1;
	int b, d, f;

	for (d = 0; d < MAX_PCIBUS_DEVS; d++)
		for (f = 0; f < MAX_PCIDEV_FNS; f++)
			pps[d][f] = NULL;

	/* start at the parent's first sibling */
	sib = di_child_node(pn);
	while (sib != DI_NODE_NIL) {
		np = did_create(mod, sib, board, bridge, rc, bover);
		if (np == NULL)
			return (-1);
		did_BDF(np, &b, &d, &f);
		pps[d][f] = np;
		if (bp == NULL)
			bp = np;
		if (pb < 0)
			pb = ((bover == TRUST_BDF) ? b : bover);
		sib = di_sibling_node(sib);
	}
	if (pb < 0 && bover < 0)
		return (0);
	if (rc >= 0)
		bn = pciexbus_declare(mod, parent, pn, ((pb < 0) ? bover : pb));
	else
		bn = pcibus_declare(mod, parent, pn, ((pb < 0) ? bover : pb));
	if (bn == NULL)
		return (-1);
	if (pb < 0)
		return (0);

	for (d = 0; d < MAX_PCIBUS_DEVS; d++) {
		for (f = 0; f < MAX_PCIDEV_FNS; f++) {
			if (pps[d][f] == NULL)
				continue;
			din = did_dinode(pps[d][f]);

			/*
			 * Try to enumerate as many devices and functions as
			 * possible.  If we fail to declare a device, break
			 * out of the function loop.
			 */
			declare_dev_and_fn(mod, bn,
			    &dn, din, board, bridge, rc, d, f, depth);
			did_rele(pps[d][f]);

			if (dn == NULL)
				break;
		}
		dn = NULL;
	}
	return (0);
}

static int
pciexbus_enum(topo_mod_t *mp, tnode_t *ptn, char *pnm, topo_instance_t min,
    topo_instance_t max)
{
	di_node_t pdn;
	int rc;
	int retval;

	/*
	 * PCI-Express; root complex shares the hostbridge's instance
	 * number.  Parent node's private data is a simple di_node_t
	 * and we have to construct our own did hash and did_t.
	 */
	rc = topo_node_instance(ptn);

	if ((pdn = topo_node_getspecific(ptn)) == DI_NODE_NIL) {
		topo_mod_dprintf(mp,
		    "Parent %s node missing private data.\n"
		    "Unable to proceed with %s enumeration.\n", pnm, PCIEX_BUS);
		return (0);
	}
	if (did_hash_init(mp) != 0)
		return (-1);
	if ((did_create(mp, pdn, 0, 0, rc, TRUST_BDF)) == NULL)
		return (-1);	/* errno already set */

	retval = pci_children_instantiate(mp, ptn, pdn, 0, 0, rc,
	    (min == max) ? min : TRUST_BDF, 0);
	did_hash_fini(mp);

	return (retval);
}

static int
pcibus_enum(topo_mod_t *mp, tnode_t *ptn, char *pnm, topo_instance_t min,
    topo_instance_t max, void *data)
{
	did_t *didp, *hbdid = (did_t *)data;
	int retval;

	/*
	 * XXTOPO: we should not be sharing private node data with another
	 * module. PCI Bus; Parent node's private data is a did_t.  We'll
	 * use the did hash established by the parent.
	 */
	did_setspecific(mp, data);

	/*
	 * If we're looking for a specific bus-instance, find the right
	 * did_t in the chain, otherwise, there should be only one did_t.
	 */
	if (min == max) {
		int b;
		didp = hbdid;
		while (didp != NULL) {
			did_BDF(didp, &b, NULL, NULL);
			if (b == min)
				break;
			didp = did_link_get(didp);
		}
		if (didp == NULL) {
			topo_mod_dprintf(mp,
			    "Parent %s node missing private data related\n"
			    "to %s instance %d.\n", pnm, PCI_BUS, min);
			topo_mod_setspecific(mp, NULL);
			return (0);
		}
	} else {
		assert(did_link_get(hbdid) == NULL);
		didp = hbdid;
	}
	retval = pci_children_instantiate(mp, ptn, did_dinode(didp),
	    did_board(didp), did_bridge(didp), did_rc(didp),
	    (min == max) ? min : TRUST_BDF, 0);

	topo_mod_setspecific(mp, NULL);

	return (retval);
}

/*ARGSUSED*/
static int
pci_enum(topo_mod_t *mod, tnode_t *ptn, const char *name,
    topo_instance_t min, topo_instance_t max, void *notused, void *data)
{
	int retval;
	char *pname;

	topo_mod_dprintf(mod, "Enumerating pci!\n");

	if (strcmp(name, PCI_BUS) != 0 && strcmp(name, PCIEX_BUS) != 0) {
		topo_mod_dprintf(mod,
		    "Currently only know how to enumerate %s or %s.\n",
		    PCI_BUS, PCIEX_BUS);
		return (0);
	}
	pname = topo_node_name(ptn);
	if (strcmp(pname, HOSTBRIDGE) != 0 && strcmp(pname, PCIEX_ROOT) != 0) {
		topo_mod_dprintf(mod,
		    "Currently can only enumerate a %s or %s directly\n",
		    PCI_BUS, PCIEX_BUS);
		topo_mod_dprintf(mod,
		    "descended from a %s or %s node.\n",
		    HOSTBRIDGE, PCIEX_ROOT);
		return (0);
	}

	if (strcmp(name, PCI_BUS) == 0) {
		retval = pcibus_enum(mod, ptn, pname, min, max, data);
	} else if (strcmp(name, PCIEX_BUS) == 0) {
		retval = pciexbus_enum(mod, ptn, pname, min, max);
	} else {
		topo_mod_dprintf(mod,
		    "Currently only know how to enumerate %s or %s not %s.\n",
		    PCI_BUS, PCIEX_BUS, name);
		return (0);
	}

	return (retval);
}

/*ARGSUSED*/
static void
pci_release(topo_mod_t *mp, tnode_t *node)
{
	topo_method_unregister_all(mp, node);

	/*
	 * node private data (did_t) for this node is destroyed in
	 * did_hash_destroy()
	 */

	topo_node_unbind(node);
}