xref: /onnv-gate/usr/src/lib/fm/topo/modules/common/pcibus/pcibus.c (revision 12126:60364f3f65c7)
13062Scindi /*
23062Scindi  * CDDL HEADER START
33062Scindi  *
43062Scindi  * The contents of this file are subject to the terms of the
53062Scindi  * Common Development and Distribution License (the "License").
63062Scindi  * You may not use this file except in compliance with the License.
73062Scindi  *
83062Scindi  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93062Scindi  * or http://www.opensolaris.org/os/licensing.
103062Scindi  * See the License for the specific language governing permissions
113062Scindi  * and limitations under the License.
123062Scindi  *
133062Scindi  * When distributing Covered Code, include this CDDL HEADER in each
143062Scindi  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153062Scindi  * If applicable, add the following below this CDDL HEADER, with the
163062Scindi  * fields enclosed by brackets "[]" replaced with your own identifying
173062Scindi  * information: Portions Copyright [yyyy] [name of copyright owner]
183062Scindi  *
193062Scindi  * CDDL HEADER END
203062Scindi  */
213062Scindi 
223062Scindi /*
2312054SStephen.Hanson@Sun.COM  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
243062Scindi  */
253062Scindi 
263062Scindi #include <sys/fm/protocol.h>
273062Scindi #include <assert.h>
283062Scindi #include <stdio.h>
293062Scindi #include <stdlib.h>
303062Scindi #include <string.h>
313062Scindi #include <strings.h>
323062Scindi #include <alloca.h>
333062Scindi #include <sys/param.h>
343062Scindi #include <sys/pci.h>
353062Scindi #include <sys/pcie.h>
363062Scindi #include <libdevinfo.h>
373062Scindi #include <libnvpair.h>
383062Scindi #include <fm/topo_mod.h>
393062Scindi #include <fm/topo_hc.h>
403062Scindi 
413062Scindi #include <hostbridge.h>
423062Scindi #include <pcibus.h>
433062Scindi #include <did.h>
443062Scindi #include <did_props.h>
453062Scindi #include <util.h>
463062Scindi 
473062Scindi extern txprop_t Bus_common_props[];
483062Scindi extern txprop_t Dev_common_props[];
493062Scindi extern txprop_t Fn_common_props[];
503062Scindi extern int Bus_propcnt;
513062Scindi extern int Dev_propcnt;
523062Scindi extern int Fn_propcnt;
533062Scindi 
543062Scindi extern int platform_pci_label(topo_mod_t *mod, tnode_t *, nvlist_t *,
553062Scindi     nvlist_t **);
563895Szx143588 extern int platform_pci_fru(topo_mod_t *mod, tnode_t *, nvlist_t *,
573895Szx143588     nvlist_t **);
583062Scindi static void pci_release(topo_mod_t *, tnode_t *);
593062Scindi static int pci_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
603062Scindi     topo_instance_t, void *, void *);
613062Scindi static int pci_label(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
623062Scindi     nvlist_t **);
633895Szx143588 static int pci_fru(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
643895Szx143588     nvlist_t **);
653062Scindi 
663062Scindi static const topo_modops_t Pci_ops =
673062Scindi 	{ pci_enum, pci_release };
683062Scindi static const topo_modinfo_t Pci_info =
693062Scindi 	{ PCI_BUS, FM_FMRI_SCHEME_HC, PCI_ENUMR_VERS, &Pci_ops };
703062Scindi 
713062Scindi static const topo_method_t Pci_methods[] = {
723062Scindi 	{ TOPO_METH_LABEL, TOPO_METH_LABEL_DESC,
733062Scindi 	    TOPO_METH_LABEL_VERSION, TOPO_STABILITY_INTERNAL, pci_label },
743895Szx143588 	{ TOPO_METH_FRU_COMPUTE, TOPO_METH_FRU_COMPUTE_DESC,
753895Szx143588 	    TOPO_METH_FRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL, pci_fru },
763062Scindi 	{ NULL }
773062Scindi };
783062Scindi 
793062Scindi int
_topo_init(topo_mod_t * modhdl,topo_version_t version)803062Scindi _topo_init(topo_mod_t *modhdl, topo_version_t version)
813062Scindi {
823062Scindi 	/*
833062Scindi 	 * Turn on module debugging output
843062Scindi 	 */
853062Scindi 	if (getenv("TOPOPCIDBG") != NULL)
863062Scindi 		topo_mod_setdebug(modhdl);
873062Scindi 	topo_mod_dprintf(modhdl, "initializing pcibus builtin\n");
883062Scindi 
893062Scindi 	if (version != PCI_ENUMR_VERS)
903062Scindi 		return (topo_mod_seterrno(modhdl, EMOD_VER_NEW));
913062Scindi 
9211050SRobert.Johnston@Sun.COM 	if (topo_mod_register(modhdl, &Pci_info, TOPO_VERSION) != 0) {
9311050SRobert.Johnston@Sun.COM 		topo_mod_dprintf(modhdl, "failed to register module");
9411050SRobert.Johnston@Sun.COM 		return (-1);
9511050SRobert.Johnston@Sun.COM 	}
963062Scindi 	topo_mod_dprintf(modhdl, "PCI Enumr initd\n");
973062Scindi 
983062Scindi 	return (0);
993062Scindi }
1003062Scindi 
1013062Scindi void
_topo_fini(topo_mod_t * modhdl)1023062Scindi _topo_fini(topo_mod_t *modhdl)
1033062Scindi {
1043062Scindi 	topo_mod_unregister(modhdl);
1053062Scindi }
1063062Scindi 
1073062Scindi static int
pci_label(topo_mod_t * mp,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)1083062Scindi pci_label(topo_mod_t *mp, tnode_t *node, topo_version_t version,
1093062Scindi     nvlist_t *in, nvlist_t **out)
1103062Scindi {
1113062Scindi 	if (version > TOPO_METH_LABEL_VERSION)
1123062Scindi 		return (topo_mod_seterrno(mp, EMOD_VER_NEW));
1133062Scindi 	return (platform_pci_label(mp, node, in, out));
1143062Scindi }
1153895Szx143588 static int
pci_fru(topo_mod_t * mp,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)1163895Szx143588 pci_fru(topo_mod_t *mp, tnode_t *node, topo_version_t version,
1173895Szx143588     nvlist_t *in, nvlist_t **out)
1183895Szx143588 {
1193895Szx143588 	if (version > TOPO_METH_FRU_COMPUTE_VERSION)
1203895Szx143588 		return (topo_mod_seterrno(mp, EMOD_VER_NEW));
1213895Szx143588 	return (platform_pci_fru(mp, node, in, out));
1223895Szx143588 }
1233062Scindi static tnode_t *
pci_tnode_create(topo_mod_t * mod,tnode_t * parent,const char * name,topo_instance_t i,void * priv)1243062Scindi pci_tnode_create(topo_mod_t *mod, tnode_t *parent,
1253062Scindi     const char *name, topo_instance_t i, void *priv)
1263062Scindi {
1273062Scindi 	tnode_t *ntn;
1283062Scindi 
1293062Scindi 	if ((ntn = tnode_create(mod, parent, name, i, priv)) == NULL)
1303062Scindi 		return (NULL);
1313062Scindi 	if (topo_method_register(mod, ntn, Pci_methods) < 0) {
1323062Scindi 		topo_mod_dprintf(mod, "topo_method_register failed: %s\n",
1333062Scindi 		    topo_strerror(topo_mod_errno(mod)));
1343062Scindi 		topo_node_unbind(ntn);
1353062Scindi 		return (NULL);
1363062Scindi 	}
1373062Scindi 	return (ntn);
1383062Scindi }
1393062Scindi 
1403062Scindi /*ARGSUSED*/
1413062Scindi static int
hostbridge_asdevice(topo_mod_t * mod,tnode_t * bus)1423062Scindi hostbridge_asdevice(topo_mod_t *mod, tnode_t *bus)
1433062Scindi {
1443062Scindi 	di_node_t di;
1453062Scindi 	tnode_t *dev32;
1463062Scindi 
1473062Scindi 	di = topo_node_getspecific(bus);
1483062Scindi 	assert(di != DI_NODE_NIL);
1493062Scindi 
1503062Scindi 	if ((dev32 = pcidev_declare(mod, bus, di, 32)) == NULL)
1513062Scindi 		return (-1);
1524328Scindi 	if (pcifn_declare(mod, dev32, di, 0) == NULL) {
1534328Scindi 		topo_node_unbind(dev32);
1543062Scindi 		return (-1);
1554328Scindi 	}
1563062Scindi 	return (0);
1573062Scindi }
1583062Scindi 
1593062Scindi tnode_t *
pciexfn_declare(topo_mod_t * mod,tnode_t * parent,di_node_t dn,topo_instance_t i)1603062Scindi pciexfn_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
1613062Scindi     topo_instance_t i)
1623062Scindi {
1633062Scindi 	did_t *pd;
1647987SErwin.Tsaur@Sun.COM 	tnode_t *ntn, *ptn;
1657987SErwin.Tsaur@Sun.COM 	di_node_t pdn;
1667987SErwin.Tsaur@Sun.COM 	uint_t class, subclass;
1677987SErwin.Tsaur@Sun.COM 	char *devtyp, *pdevtyp;
1687987SErwin.Tsaur@Sun.COM 	int pcie_devtyp, pexcap;
1697987SErwin.Tsaur@Sun.COM 	boolean_t dev_is_pcie, pdev_is_pcie;
1707987SErwin.Tsaur@Sun.COM 
1717987SErwin.Tsaur@Sun.COM 	/* We need the parent's dev info node for some of the info */
1727987SErwin.Tsaur@Sun.COM 	ptn = find_predecessor(parent, PCIEX_FUNCTION);
1737987SErwin.Tsaur@Sun.COM 	/* If this is the first child under root, get root's ptn */
1747987SErwin.Tsaur@Sun.COM 	if (ptn == NULL)
1757987SErwin.Tsaur@Sun.COM 		ptn = find_predecessor(parent, PCIEX_ROOT);
1767987SErwin.Tsaur@Sun.COM 	if (ptn == NULL)
1777987SErwin.Tsaur@Sun.COM 		return (NULL);
1787987SErwin.Tsaur@Sun.COM 	pdn = topo_node_getspecific(ptn);
1797987SErwin.Tsaur@Sun.COM 
1807987SErwin.Tsaur@Sun.COM 	/* Get the required info to populate the excap */
1817987SErwin.Tsaur@Sun.COM 	(void) pci_classcode_get(mod, dn, &class, &subclass);
1827987SErwin.Tsaur@Sun.COM 	devtyp = pci_devtype_get(mod, dn);
1837987SErwin.Tsaur@Sun.COM 	pdevtyp = pci_devtype_get(mod, pdn);
1847987SErwin.Tsaur@Sun.COM 	pexcap = pciex_cap_get(mod, pdn);
1857987SErwin.Tsaur@Sun.COM 
1867987SErwin.Tsaur@Sun.COM 	dev_is_pcie = devtyp && (strcmp(devtyp, "pciex") == 0);
1877987SErwin.Tsaur@Sun.COM 	pdev_is_pcie = pdevtyp && (strcmp(pdevtyp, "pciex") == 0);
1887987SErwin.Tsaur@Sun.COM 
1897987SErwin.Tsaur@Sun.COM 	/*
1907987SErwin.Tsaur@Sun.COM 	 * Populate the excap with correct PCIe device type.
1917987SErwin.Tsaur@Sun.COM 	 *
1927987SErwin.Tsaur@Sun.COM 	 * Device	Parent		Device		Parent	Device
1937987SErwin.Tsaur@Sun.COM 	 * excap	device-type	device-type	excap	Class Code
1947987SErwin.Tsaur@Sun.COM 	 * -------------------------------------------------------------------
1957987SErwin.Tsaur@Sun.COM 	 * PCI(default)	pci		N/A		N/A	!= bridge
1967987SErwin.Tsaur@Sun.COM 	 * PCIe		pciex		N/A		N/A	!= bridge
1977987SErwin.Tsaur@Sun.COM 	 * Root Port	Defined in hostbridge
1987987SErwin.Tsaur@Sun.COM 	 * Switch Up	pciex		pciex		!= up	= bridge
1997987SErwin.Tsaur@Sun.COM 	 * Switch Down	pciex		pciex		= up	= bridge
2007987SErwin.Tsaur@Sun.COM 	 * PCIe-PCI	pciex		pci		N/A	= bridge
2017987SErwin.Tsaur@Sun.COM 	 * PCI-PCIe	pci		pciex		N/A	= bridge
2027987SErwin.Tsaur@Sun.COM 	 */
2037987SErwin.Tsaur@Sun.COM 	pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_PCI_DEV;
2047987SErwin.Tsaur@Sun.COM 	if (class == PCI_CLASS_BRIDGE && subclass == PCI_BRIDGE_PCI) {
2057987SErwin.Tsaur@Sun.COM 		if (pdev_is_pcie) {
2067987SErwin.Tsaur@Sun.COM 			if (dev_is_pcie) {
2077987SErwin.Tsaur@Sun.COM 				if (pexcap != PCIE_PCIECAP_DEV_TYPE_UP)
2087987SErwin.Tsaur@Sun.COM 					pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_UP;
2097987SErwin.Tsaur@Sun.COM 				else
2107987SErwin.Tsaur@Sun.COM 					pcie_devtyp =
2117987SErwin.Tsaur@Sun.COM 					    PCIE_PCIECAP_DEV_TYPE_DOWN;
2127987SErwin.Tsaur@Sun.COM 			} else {
2137987SErwin.Tsaur@Sun.COM 				pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_PCIE2PCI;
2147987SErwin.Tsaur@Sun.COM 			}
2157987SErwin.Tsaur@Sun.COM 		} else {
2167987SErwin.Tsaur@Sun.COM 			if (dev_is_pcie)
2177987SErwin.Tsaur@Sun.COM 				pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_PCI2PCIE;
2187987SErwin.Tsaur@Sun.COM 		}
2197987SErwin.Tsaur@Sun.COM 	} else {
2207987SErwin.Tsaur@Sun.COM 		if (pdev_is_pcie)
2217987SErwin.Tsaur@Sun.COM 			pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_PCIE_DEV;
2227987SErwin.Tsaur@Sun.COM 	}
2233062Scindi 
2243062Scindi 	if ((pd = did_find(mod, dn)) == NULL)
2253062Scindi 		return (NULL);
2267987SErwin.Tsaur@Sun.COM 	did_excap_set(pd, pcie_devtyp);
2277987SErwin.Tsaur@Sun.COM 
2283062Scindi 	if ((ntn = pci_tnode_create(mod, parent, PCIEX_FUNCTION, i, dn))
2293062Scindi 	    == NULL)
2303062Scindi 		return (NULL);
2313062Scindi 	if (did_props_set(ntn, pd, Fn_common_props, Fn_propcnt) < 0) {
2323062Scindi 		topo_node_unbind(ntn);
2333062Scindi 		return (NULL);
2343062Scindi 	}
2353062Scindi 	/*
2363062Scindi 	 * We may find pci-express buses or plain-pci buses beneath a function
2373062Scindi 	 */
2383062Scindi 	if (child_range_add(mod, ntn, PCIEX_BUS, 0, MAX_HB_BUSES) < 0) {
2394328Scindi 		topo_node_unbind(ntn);
2403062Scindi 		return (NULL);
2413062Scindi 	}
2423062Scindi 	if (child_range_add(mod, ntn, PCI_BUS, 0, MAX_HB_BUSES) < 0) {
2434328Scindi 		topo_node_range_destroy(ntn, PCIEX_BUS);
2444328Scindi 		topo_node_unbind(ntn);
2453062Scindi 		return (NULL);
2463062Scindi 	}
2473062Scindi 	return (ntn);
2483062Scindi }
2493062Scindi 
2503062Scindi tnode_t *
pciexdev_declare(topo_mod_t * mod,tnode_t * parent,di_node_t dn,topo_instance_t i)2513062Scindi pciexdev_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
2523062Scindi     topo_instance_t i)
2533062Scindi {
2543062Scindi 	did_t *pd;
2553062Scindi 	tnode_t *ntn;
2563062Scindi 
2573062Scindi 	if ((pd = did_find(mod, dn)) == NULL)
2583062Scindi 		return (NULL);
2593895Szx143588 	did_settnode(pd, parent);
2603895Szx143588 
2613062Scindi 	if ((ntn = pci_tnode_create(mod, parent, PCIEX_DEVICE, i, dn)) == NULL)
2623062Scindi 		return (NULL);
2633062Scindi 	if (did_props_set(ntn, pd, Dev_common_props, Dev_propcnt) < 0) {
2643062Scindi 		topo_node_unbind(ntn);
2653062Scindi 		return (NULL);
2663062Scindi 	}
2673062Scindi 
2683062Scindi 	/*
2693062Scindi 	 * We can expect to find pci-express functions beneath the device
2703062Scindi 	 */
2713062Scindi 	if (child_range_add(mod,
2723062Scindi 	    ntn, PCIEX_FUNCTION, 0, MAX_PCIDEV_FNS) < 0) {
2734328Scindi 		topo_node_unbind(ntn);
2743062Scindi 		return (NULL);
2753062Scindi 	}
2763062Scindi 	return (ntn);
2773062Scindi }
2783062Scindi 
2793062Scindi tnode_t *
pciexbus_declare(topo_mod_t * mod,tnode_t * parent,di_node_t dn,topo_instance_t i)2803062Scindi pciexbus_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
2813062Scindi     topo_instance_t i)
2823062Scindi {
2833062Scindi 	did_t *pd;
2843062Scindi 	tnode_t *ntn;
2853062Scindi 
2863062Scindi 	if ((pd = did_find(mod, dn)) == NULL)
2873062Scindi 		return (NULL);
2885204Sstephh 	did_settnode(pd, parent);
2893062Scindi 	if ((ntn = pci_tnode_create(mod, parent, PCIEX_BUS, i, dn)) == NULL)
2903062Scindi 		return (NULL);
2913062Scindi 	if (did_props_set(ntn, pd, Bus_common_props, Bus_propcnt) < 0) {
2923062Scindi 		topo_node_unbind(ntn);
2933062Scindi 		return (NULL);
2943062Scindi 	}
2953062Scindi 	/*
2963062Scindi 	 * We can expect to find pci-express devices beneath the bus
2973062Scindi 	 */
2983062Scindi 	if (child_range_add(mod,
2993062Scindi 	    ntn, PCIEX_DEVICE, 0, MAX_PCIBUS_DEVS) < 0) {
3004328Scindi 		topo_node_unbind(ntn);
3013062Scindi 		return (NULL);
3023062Scindi 	}
3033062Scindi 	return (ntn);
3043062Scindi }
3053062Scindi 
3063062Scindi tnode_t *
pcifn_declare(topo_mod_t * mod,tnode_t * parent,di_node_t dn,topo_instance_t i)3073062Scindi pcifn_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
3083062Scindi     topo_instance_t i)
3093062Scindi {
3103062Scindi 	did_t *pd;
3113062Scindi 	tnode_t *ntn;
3123062Scindi 
3133062Scindi 	if ((pd = did_find(mod, dn)) == NULL)
3143062Scindi 		return (NULL);
3157987SErwin.Tsaur@Sun.COM 	did_excap_set(pd, PCIE_PCIECAP_DEV_TYPE_PCI_DEV);
3167987SErwin.Tsaur@Sun.COM 
3173062Scindi 	if ((ntn = pci_tnode_create(mod, parent, PCI_FUNCTION, i, dn)) == NULL)
3183062Scindi 		return (NULL);
3193062Scindi 	if (did_props_set(ntn, pd, Fn_common_props, Fn_propcnt) < 0) {
3203062Scindi 		topo_node_unbind(ntn);
3213062Scindi 		return (NULL);
3223062Scindi 	}
3233062Scindi 	/*
3243062Scindi 	 * We may find pci buses beneath a function
3253062Scindi 	 */
3263062Scindi 	if (child_range_add(mod, ntn, PCI_BUS, 0, MAX_HB_BUSES) < 0) {
3273062Scindi 		topo_node_unbind(ntn);
3283062Scindi 		return (NULL);
3293062Scindi 	}
3303062Scindi 	return (ntn);
3313062Scindi }
3323062Scindi 
3333062Scindi tnode_t *
pcidev_declare(topo_mod_t * mod,tnode_t * parent,di_node_t dn,topo_instance_t i)3343062Scindi pcidev_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
3353062Scindi     topo_instance_t i)
3363062Scindi {
3373062Scindi 	did_t *pd;
3383062Scindi 	tnode_t *ntn;
3393062Scindi 
3403062Scindi 	if ((pd = did_find(mod, dn)) == NULL)
3413062Scindi 		return (NULL);
3423895Szx143588 	/* remember parent tnode */
3433895Szx143588 	did_settnode(pd, parent);
3443895Szx143588 
3453062Scindi 	if ((ntn = pci_tnode_create(mod, parent, PCI_DEVICE, i, dn)) == NULL)
3463062Scindi 		return (NULL);
3473062Scindi 	if (did_props_set(ntn, pd, Dev_common_props, Dev_propcnt) < 0) {
3483062Scindi 		topo_node_unbind(ntn);
3493062Scindi 		return (NULL);
3503062Scindi 	}
3513062Scindi 
3523062Scindi 	/*
3533062Scindi 	 * We can expect to find pci functions beneath the device
3543062Scindi 	 */
3553062Scindi 	if (child_range_add(mod, ntn, PCI_FUNCTION, 0, MAX_PCIDEV_FNS) < 0) {
3563062Scindi 		topo_node_unbind(ntn);
3573062Scindi 		return (NULL);
3583062Scindi 	}
3593062Scindi 	return (ntn);
3603062Scindi }
3613062Scindi 
3623062Scindi tnode_t *
pcibus_declare(topo_mod_t * mod,tnode_t * parent,di_node_t dn,topo_instance_t i)3633062Scindi pcibus_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
3643062Scindi     topo_instance_t i)
3653062Scindi {
3663062Scindi 	did_t *pd;
3673062Scindi 	tnode_t *ntn;
3683062Scindi 	int hbchild = 0;
3693062Scindi 
3703062Scindi 	if ((pd = did_find(mod, dn)) == NULL)
3713062Scindi 		return (NULL);
3725204Sstephh 	did_settnode(pd, parent);
3733062Scindi 	if ((ntn = pci_tnode_create(mod, parent, PCI_BUS, i, dn)) == NULL)
3743062Scindi 		return (NULL);
3753062Scindi 	/*
3763062Scindi 	 * If our devinfo node is lacking certain information of its
3773062Scindi 	 * own, and our parent topology node is a hostbridge, we may
3783062Scindi 	 * need/want to inherit information available in the
3793062Scindi 	 * hostbridge node's private data.
3803062Scindi 	 */
3813062Scindi 	if (strcmp(topo_node_name(parent), HOSTBRIDGE) == 0)
3823062Scindi 		hbchild = 1;
3833062Scindi 	if (did_props_set(ntn, pd, Bus_common_props, Bus_propcnt) < 0) {
3843062Scindi 		topo_node_unbind(ntn);
3853062Scindi 		return (NULL);
3863062Scindi 	}
3873062Scindi 	/*
3883062Scindi 	 * We can expect to find pci devices beneath the bus
3893062Scindi 	 */
3903062Scindi 	if (child_range_add(mod, ntn, PCI_DEVICE, 0, MAX_PCIBUS_DEVS) < 0) {
3913062Scindi 		topo_node_unbind(ntn);
3923062Scindi 		return (NULL);
3933062Scindi 	}
3943062Scindi 	/*
3953062Scindi 	 * On each bus child of the hostbridge, we represent the
3963062Scindi 	 * hostbridge as a device outside the range of legal device
3973062Scindi 	 * numbers.
3983062Scindi 	 */
3993062Scindi 	if (hbchild == 1) {
4003062Scindi 		if (hostbridge_asdevice(mod, ntn) < 0) {
4013062Scindi 			topo_node_range_destroy(ntn, PCI_DEVICE);
4023062Scindi 			topo_node_unbind(ntn);
4033062Scindi 			return (NULL);
4043062Scindi 		}
4053062Scindi 	}
4063062Scindi 	return (ntn);
4073062Scindi }
4083062Scindi 
4093062Scindi static int
pci_bridge_declare(topo_mod_t * mod,tnode_t * fn,di_node_t din,int board,int bridge,int rc,int depth)4103062Scindi pci_bridge_declare(topo_mod_t *mod, tnode_t *fn, di_node_t din, int board,
4113062Scindi     int bridge, int rc, int depth)
4123062Scindi {
4137987SErwin.Tsaur@Sun.COM 	int err;
4147987SErwin.Tsaur@Sun.COM 	char *devtyp;
4153062Scindi 
4167987SErwin.Tsaur@Sun.COM 	devtyp = pci_devtype_get(mod, din);
4177987SErwin.Tsaur@Sun.COM 	/* Check if the children are PCI or PCIe */
4189921SKrishna.Elango@Sun.COM 	if (devtyp && (strcmp(devtyp, "pciex") == 0))
4193062Scindi 		err = pci_children_instantiate(mod, fn, din, board, bridge,
4203062Scindi 		    rc, TRUST_BDF, depth + 1);
4213062Scindi 	else
4223062Scindi 		err = pci_children_instantiate(mod, fn, din, board, bridge,
4233062Scindi 		    rc - TO_PCI, TRUST_BDF, depth + 1);
4243062Scindi 	return (err);
4253062Scindi }
4263062Scindi 
4274328Scindi 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)4283062Scindi declare_dev_and_fn(topo_mod_t *mod, tnode_t *bus, tnode_t **dev, di_node_t din,
4293062Scindi     int board, int bridge, int rc, int devno, int fnno, int depth)
4303062Scindi {
431*12126SHyon.Kim@Sun.COM 	int dcnt = 0, rcnt;
432*12126SHyon.Kim@Sun.COM 	char *propstr;
4333062Scindi 	tnode_t *fn;
4343062Scindi 	uint_t class, subclass;
4355060Syc148097 	uint_t vid, did;
4366326Ssd77468 	did_t *dp = NULL;
4373062Scindi 
4383062Scindi 	if (*dev == NULL) {
4393062Scindi 		if (rc >= 0)
4403062Scindi 			*dev = pciexdev_declare(mod, bus, din, devno);
4413062Scindi 		else
4423062Scindi 			*dev = pcidev_declare(mod, bus, din, devno);
4433062Scindi 		if (*dev == NULL)
4444328Scindi 			return;
4454328Scindi 		++dcnt;
4463062Scindi 	}
4473062Scindi 	if (rc >= 0)
4483062Scindi 		fn = pciexfn_declare(mod, *dev, din, fnno);
4493062Scindi 	else
4503062Scindi 		fn = pcifn_declare(mod, *dev, din, fnno);
4514328Scindi 
4524328Scindi 	if (fn == NULL) {
4534328Scindi 		if (dcnt) {
4544328Scindi 			topo_node_unbind(*dev);
4554328Scindi 			*dev = NULL;
4564328Scindi 		}
4574328Scindi 		return;
4584328Scindi 	}
4594328Scindi 
4604328Scindi 	if (pci_classcode_get(mod, din, &class, &subclass) < 0) {
4614328Scindi 		topo_node_unbind(fn);
4624328Scindi 		if (dcnt)
4634328Scindi 			topo_node_unbind(*dev);
4644328Scindi 		return;
4654328Scindi 	}
4663062Scindi 
4673062Scindi 	/*
4683062Scindi 	 * This function may be a bridge.  If not, check for a possible
4693062Scindi 	 * topology map file and kick off its enumeration of lower-level
4703062Scindi 	 * devices.
4713062Scindi 	 */
4725060Syc148097 	if (class == PCI_CLASS_BRIDGE && subclass == PCI_BRIDGE_PCI) {
4734328Scindi 		(void) pci_bridge_declare(mod, fn, din, board, bridge, rc,
4743062Scindi 		    depth);
4755060Syc148097 	}
4766326Ssd77468 
4775060Syc148097 	/*
4786326Ssd77468 	 * Check for a Neptune-based NIC. This could either be a Neptune
4796326Ssd77468 	 * adapter card or an Neptune ASIC on a board (e.g. motherboard)
4806326Ssd77468 	 *
4816326Ssd77468 	 * For Netpune adapter cards, use xfp-hc-topology.xml to expand
4826326Ssd77468 	 * topology to include the XFP optical module, which is a FRU on
4836326Ssd77468 	 * the Neptune based 10giga fiber NICs.
4846326Ssd77468 	 *
4856326Ssd77468 	 * For Neptune ASICs, use the XAUI enumerator to expand topology.
4866326Ssd77468 	 * The 10giga ports are externalized by a XAUI cards, which
4876326Ssd77468 	 * are FRUs. The XAUI enumerator in turn instantiates the XFP
4886326Ssd77468 	 * optical module FRUs.
4895060Syc148097 	 */
4905060Syc148097 	else if (class == PCI_CLASS_NET &&
4915060Syc148097 	    di_uintprop_get(mod, din, DI_VENDIDPROP, &vid) >= 0 &&
4925060Syc148097 	    di_uintprop_get(mod, din, DI_DEVIDPROP, &did) >= 0) {
4935060Syc148097 		if (vid == SUN_VENDOR_ID && did == NEPTUNE_DEVICE_ID) {
4946326Ssd77468 			/*
4956326Ssd77468 			 * Is this an adapter card? Check the bus's physlot
4966326Ssd77468 			 */
4976326Ssd77468 			dp = did_find(mod, topo_node_getspecific(bus));
4988216STarik.Soydan@Sun.COM 			if (did_physlot(dp) >= 0) {
4996326Ssd77468 				topo_mod_dprintf(mod, "Found Neptune slot\n");
5006326Ssd77468 				(void) topo_mod_enummap(mod, fn,
5016326Ssd77468 				    "xfp", FM_FMRI_SCHEME_HC);
5026326Ssd77468 			} else {
5036326Ssd77468 				topo_mod_dprintf(mod, "Found Neptune ASIC\n");
5046326Ssd77468 				if (topo_mod_load(mod, XAUI, TOPO_VERSION) ==
5056326Ssd77468 				    NULL) {
5066326Ssd77468 					topo_mod_dprintf(mod, "pcibus enum "
5076326Ssd77468 					    "could not load xaui enum\n");
50811050SRobert.Johnston@Sun.COM 					(void) topo_mod_seterrno(mod,
5096326Ssd77468 					    EMOD_PARTIAL_ENUM);
5106326Ssd77468 					return;
5116326Ssd77468 				} else {
5126326Ssd77468 					if (topo_node_range_create(mod, fn,
5136326Ssd77468 					    XAUI, 0, 1) < 0) {
5146326Ssd77468 						topo_mod_dprintf(mod,
5156326Ssd77468 						    "child_range_add for "
5166326Ssd77468 						    "XAUI failed: %s\n",
5176326Ssd77468 						    topo_strerror(
5186326Ssd77468 						    topo_mod_errno(mod)));
5196326Ssd77468 						return;
5206326Ssd77468 					}
5216326Ssd77468 					(void) topo_mod_enumerate(mod, fn,
5226326Ssd77468 					    XAUI, XAUI, fnno, fnno, fn);
5236326Ssd77468 				}
5246326Ssd77468 			}
5255060Syc148097 		}
526*12126SHyon.Kim@Sun.COM 	} else if (class == PCI_CLASS_MASS) {
527*12126SHyon.Kim@Sun.COM 		di_node_t cn;
528*12126SHyon.Kim@Sun.COM 		int niports = 0;
529*12126SHyon.Kim@Sun.COM 		extern void pci_iports_instantiate(topo_mod_t *, tnode_t *,
530*12126SHyon.Kim@Sun.COM 		    di_node_t, int);
531*12126SHyon.Kim@Sun.COM 		extern void pci_receptacle_instantiate(topo_mod_t *, tnode_t *,
532*12126SHyon.Kim@Sun.COM 		    di_node_t);
533*12126SHyon.Kim@Sun.COM 
534*12126SHyon.Kim@Sun.COM 		for (cn = di_child_node(din); cn != DI_NODE_NIL;
535*12126SHyon.Kim@Sun.COM 		    cn = di_sibling_node(cn)) {
536*12126SHyon.Kim@Sun.COM 			if (strcmp(di_node_name(cn), IPORT) == 0)
537*12126SHyon.Kim@Sun.COM 				niports++;
538*12126SHyon.Kim@Sun.COM 		}
539*12126SHyon.Kim@Sun.COM 		if (niports > 0)
540*12126SHyon.Kim@Sun.COM 			pci_iports_instantiate(mod, fn, din, niports);
541*12126SHyon.Kim@Sun.COM 
542*12126SHyon.Kim@Sun.COM 		if ((rcnt = di_prop_lookup_strings(DDI_DEV_T_ANY, din,
543*12126SHyon.Kim@Sun.COM 		    DI_RECEPTACLE_PHYMASK, &propstr)) > 0) {
544*12126SHyon.Kim@Sun.COM 			if (topo_node_range_create(mod, fn, RECEPTACLE, 0,
545*12126SHyon.Kim@Sun.COM 			    rcnt) >= 0)
546*12126SHyon.Kim@Sun.COM 				pci_receptacle_instantiate(mod, fn, din);
547*12126SHyon.Kim@Sun.COM 		}
5485060Syc148097 	}
5493062Scindi }
5503062Scindi 
5513062Scindi 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)5523062Scindi pci_children_instantiate(topo_mod_t *mod, tnode_t *parent, di_node_t pn,
5533062Scindi     int board, int bridge, int rc, int bover, int depth)
5543062Scindi {
5553062Scindi 	did_t *pps[MAX_PCIBUS_DEVS][MAX_PCIDEV_FNS];
5563062Scindi 	did_t *bp = NULL;
5573062Scindi 	did_t *np;
5583062Scindi 	di_node_t sib;
5593062Scindi 	di_node_t din;
5603062Scindi 	tnode_t *bn = NULL;
5613062Scindi 	tnode_t *dn = NULL;
5623062Scindi 	int pb = -1;
5633062Scindi 	int b, d, f;
5643062Scindi 
5653062Scindi 	for (d = 0; d < MAX_PCIBUS_DEVS; d++)
5663062Scindi 		for (f = 0; f < MAX_PCIDEV_FNS; f++)
5673062Scindi 			pps[d][f] = NULL;
5683062Scindi 
5693062Scindi 	/* start at the parent's first sibling */
5703062Scindi 	sib = di_child_node(pn);
5713062Scindi 	while (sib != DI_NODE_NIL) {
5723062Scindi 		np = did_create(mod, sib, board, bridge, rc, bover);
5733062Scindi 		if (np == NULL)
5743062Scindi 			return (-1);
5753062Scindi 		did_BDF(np, &b, &d, &f);
5763062Scindi 		pps[d][f] = np;
5773062Scindi 		if (bp == NULL)
5783062Scindi 			bp = np;
5793062Scindi 		if (pb < 0)
5803062Scindi 			pb = ((bover == TRUST_BDF) ? b : bover);
5813062Scindi 		sib = di_sibling_node(sib);
5823062Scindi 	}
5833062Scindi 	if (pb < 0 && bover < 0)
5843062Scindi 		return (0);
5853062Scindi 	if (rc >= 0)
5863062Scindi 		bn = pciexbus_declare(mod, parent, pn, ((pb < 0) ? bover : pb));
5873062Scindi 	else
5883062Scindi 		bn = pcibus_declare(mod, parent, pn, ((pb < 0) ? bover : pb));
5893062Scindi 	if (bn == NULL)
5903062Scindi 		return (-1);
5913062Scindi 	if (pb < 0)
5923062Scindi 		return (0);
5933062Scindi 
5943062Scindi 	for (d = 0; d < MAX_PCIBUS_DEVS; d++) {
5953062Scindi 		for (f = 0; f < MAX_PCIDEV_FNS; f++) {
5963062Scindi 			if (pps[d][f] == NULL)
5973062Scindi 				continue;
5983062Scindi 			din = did_dinode(pps[d][f]);
5994328Scindi 
6003062Scindi 			/*
6014328Scindi 			 * Try to enumerate as many devices and functions as
6024328Scindi 			 * possible.  If we fail to declare a device, break
6034328Scindi 			 * out of the function loop.
6043062Scindi 			 */
6054328Scindi 			declare_dev_and_fn(mod, bn,
6063062Scindi 			    &dn, din, board, bridge, rc, d, f, depth);
6073062Scindi 			did_rele(pps[d][f]);
6084328Scindi 
6094328Scindi 			if (dn == NULL)
6104328Scindi 				break;
6113062Scindi 		}
6123062Scindi 		dn = NULL;
6133062Scindi 	}
6143062Scindi 	return (0);
6153062Scindi }
6163062Scindi 
6173062Scindi static int
pciexbus_enum(topo_mod_t * mp,tnode_t * ptn,char * pnm,topo_instance_t min,topo_instance_t max)6183062Scindi pciexbus_enum(topo_mod_t *mp, tnode_t *ptn, char *pnm, topo_instance_t min,
6193062Scindi     topo_instance_t max)
6203062Scindi {
6213062Scindi 	di_node_t pdn;
62212054SStephen.Hanson@Sun.COM 	int rc, hb;
62312054SStephen.Hanson@Sun.COM 	tnode_t *hbtn;
6243062Scindi 	int retval;
6253062Scindi 
6263062Scindi 	/*
62712054SStephen.Hanson@Sun.COM 	 * PCI-Express; parent node's private data is a simple di_node_t
6283062Scindi 	 * and we have to construct our own did hash and did_t.
6293062Scindi 	 */
6303062Scindi 	rc = topo_node_instance(ptn);
63112054SStephen.Hanson@Sun.COM 	if ((hbtn = topo_node_parent(ptn)) != NULL)
63212054SStephen.Hanson@Sun.COM 		hb = topo_node_instance(hbtn);
63312054SStephen.Hanson@Sun.COM 	else
63412054SStephen.Hanson@Sun.COM 		hb = rc;
6353062Scindi 
6363062Scindi 	if ((pdn = topo_node_getspecific(ptn)) == DI_NODE_NIL) {
6373062Scindi 		topo_mod_dprintf(mp,
6383062Scindi 		    "Parent %s node missing private data.\n"
6393062Scindi 		    "Unable to proceed with %s enumeration.\n", pnm, PCIEX_BUS);
6403062Scindi 		return (0);
6413062Scindi 	}
6425017Seschrock 	if (did_hash_init(mp) != 0)
6435017Seschrock 		return (-1);
64412054SStephen.Hanson@Sun.COM 	if ((did_create(mp, pdn, 0, hb, rc, TRUST_BDF)) == NULL)
6453062Scindi 		return (-1);	/* errno already set */
6463895Szx143588 
64712054SStephen.Hanson@Sun.COM 	retval = pci_children_instantiate(mp, ptn, pdn, 0, hb, rc,
6483062Scindi 	    (min == max) ? min : TRUST_BDF, 0);
6493062Scindi 	did_hash_fini(mp);
6503062Scindi 
6513062Scindi 	return (retval);
6523062Scindi }
6533062Scindi 
6543062Scindi static int
pcibus_enum(topo_mod_t * mp,tnode_t * ptn,char * pnm,topo_instance_t min,topo_instance_t max,void * data)6553062Scindi pcibus_enum(topo_mod_t *mp, tnode_t *ptn, char *pnm, topo_instance_t min,
6563062Scindi     topo_instance_t max, void *data)
6573062Scindi {
6583062Scindi 	did_t *didp, *hbdid = (did_t *)data;
6593062Scindi 	int retval;
6603062Scindi 
6613062Scindi 	/*
6623062Scindi 	 * XXTOPO: we should not be sharing private node data with another
6633062Scindi 	 * module. PCI Bus; Parent node's private data is a did_t.  We'll
6643062Scindi 	 * use the did hash established by the parent.
6653062Scindi 	 */
6663062Scindi 	did_setspecific(mp, data);
6673062Scindi 
6683062Scindi 	/*
6693062Scindi 	 * If we're looking for a specific bus-instance, find the right
6703062Scindi 	 * did_t in the chain, otherwise, there should be only one did_t.
6713062Scindi 	 */
6723062Scindi 	if (min == max) {
6733062Scindi 		int b;
6743062Scindi 		didp = hbdid;
6753062Scindi 		while (didp != NULL) {
6763062Scindi 			did_BDF(didp, &b, NULL, NULL);
6773062Scindi 			if (b == min)
6783062Scindi 				break;
6793062Scindi 			didp = did_link_get(didp);
6803062Scindi 		}
6813062Scindi 		if (didp == NULL) {
6823062Scindi 			topo_mod_dprintf(mp,
6833062Scindi 			    "Parent %s node missing private data related\n"
6843062Scindi 			    "to %s instance %d.\n", pnm, PCI_BUS, min);
6853062Scindi 			topo_mod_setspecific(mp, NULL);
6863062Scindi 			return (0);
6873062Scindi 		}
6883062Scindi 	} else {
6893062Scindi 		assert(did_link_get(hbdid) == NULL);
6903062Scindi 		didp = hbdid;
6913062Scindi 	}
6923062Scindi 	retval = pci_children_instantiate(mp, ptn, did_dinode(didp),
6933062Scindi 	    did_board(didp), did_bridge(didp), did_rc(didp),
6943062Scindi 	    (min == max) ? min : TRUST_BDF, 0);
6953062Scindi 
6963062Scindi 	topo_mod_setspecific(mp, NULL);
6973062Scindi 
6983062Scindi 	return (retval);
6993062Scindi }
7003062Scindi 
7013062Scindi /*ARGSUSED*/
7023062Scindi 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)7033062Scindi pci_enum(topo_mod_t *mod, tnode_t *ptn, const char *name,
7043062Scindi     topo_instance_t min, topo_instance_t max, void *notused, void *data)
7053062Scindi {
7063062Scindi 	int retval;
7073062Scindi 	char *pname;
7083062Scindi 
7093062Scindi 	topo_mod_dprintf(mod, "Enumerating pci!\n");
7103062Scindi 
7113062Scindi 	if (strcmp(name, PCI_BUS) != 0 && strcmp(name, PCIEX_BUS) != 0) {
7123062Scindi 		topo_mod_dprintf(mod,
7133062Scindi 		    "Currently only know how to enumerate %s or %s.\n",
7143062Scindi 		    PCI_BUS, PCIEX_BUS);
7153062Scindi 		return (0);
7163062Scindi 	}
7173062Scindi 	pname = topo_node_name(ptn);
7183062Scindi 	if (strcmp(pname, HOSTBRIDGE) != 0 && strcmp(pname, PCIEX_ROOT) != 0) {
7193062Scindi 		topo_mod_dprintf(mod,
7203062Scindi 		    "Currently can only enumerate a %s or %s directly\n",
7213062Scindi 		    PCI_BUS, PCIEX_BUS);
7223062Scindi 		topo_mod_dprintf(mod,
7233062Scindi 		    "descended from a %s or %s node.\n",
7243062Scindi 		    HOSTBRIDGE, PCIEX_ROOT);
7253062Scindi 		return (0);
7263062Scindi 	}
7273062Scindi 
7283062Scindi 	if (strcmp(name, PCI_BUS) == 0) {
7293062Scindi 		retval = pcibus_enum(mod, ptn, pname, min, max, data);
7303062Scindi 	} else if (strcmp(name, PCIEX_BUS) == 0) {
7313062Scindi 		retval = pciexbus_enum(mod, ptn, pname, min, max);
7323062Scindi 	} else {
7333062Scindi 		topo_mod_dprintf(mod,
7343062Scindi 		    "Currently only know how to enumerate %s or %s not %s.\n",
7353062Scindi 		    PCI_BUS, PCIEX_BUS, name);
7363062Scindi 		return (0);
7373062Scindi 	}
7383062Scindi 
7393062Scindi 	return (retval);
7403062Scindi }
7413062Scindi 
7423062Scindi /*ARGSUSED*/
7433062Scindi static void
pci_release(topo_mod_t * mp,tnode_t * node)7443062Scindi pci_release(topo_mod_t *mp, tnode_t *node)
7453062Scindi {
7463062Scindi 	topo_method_unregister_all(mp, node);
7473062Scindi 
7483062Scindi 	/*
7493062Scindi 	 * node private data (did_t) for this node is destroyed in
7503062Scindi 	 * did_hash_destroy()
7513062Scindi 	 */
7523062Scindi 
7533062Scindi 	topo_node_unbind(node);
7543062Scindi }
755