xref: /onnv-gate/usr/src/lib/fm/topo/modules/i86pc/x86pi/x86pi_hostbridge.c (revision 12832:f4172e28e09f)
110942STom.Pothier@Sun.COM /*
210942STom.Pothier@Sun.COM  * CDDL HEADER START
310942STom.Pothier@Sun.COM  *
410942STom.Pothier@Sun.COM  * The contents of this file are subject to the terms of the
510942STom.Pothier@Sun.COM  * Common Development and Distribution License (the "License").
610942STom.Pothier@Sun.COM  * You may not use this file except in compliance with the License.
710942STom.Pothier@Sun.COM  *
810942STom.Pothier@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910942STom.Pothier@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010942STom.Pothier@Sun.COM  * See the License for the specific language governing permissions
1110942STom.Pothier@Sun.COM  * and limitations under the License.
1210942STom.Pothier@Sun.COM  *
1310942STom.Pothier@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410942STom.Pothier@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510942STom.Pothier@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610942STom.Pothier@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710942STom.Pothier@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810942STom.Pothier@Sun.COM  *
1910942STom.Pothier@Sun.COM  * CDDL HEADER END
2010942STom.Pothier@Sun.COM  */
2110942STom.Pothier@Sun.COM 
2210942STom.Pothier@Sun.COM /*
2312355SSean.Ye@Sun.COM  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2410942STom.Pothier@Sun.COM  */
2510942STom.Pothier@Sun.COM 
2610942STom.Pothier@Sun.COM /*
2710942STom.Pothier@Sun.COM  * i86pc Generic hostbridge/pciex/pci enumerator
2810942STom.Pothier@Sun.COM  *
2910942STom.Pothier@Sun.COM  * hostbridge/pciexrc/pcibus topo nodes are created per SMBIOS type 138
3010942STom.Pothier@Sun.COM  * (SUN_OEM_PCIEXRC) records.   Each type 138 record can either represent
3110942STom.Pothier@Sun.COM  * a hostbridge or a pciexrc/pcibus determined by whether it points to
3210942STom.Pothier@Sun.COM  * a baseboard record or another type 138 record.
3310942STom.Pothier@Sun.COM  *
3410942STom.Pothier@Sun.COM  * x86pi_gen_hbr() is called when a new hostbridge node needs to be created..
3510942STom.Pothier@Sun.COM  * It then searches all the type 138 records that connected to it.  For each
3610942STom.Pothier@Sun.COM  * of the records, bdf is compared to find a matching di_node.  If the
3710942STom.Pothier@Sun.COM  * di_node is a pciex root port, a pciexrc (bad name!) node will be created.
3810942STom.Pothier@Sun.COM  * When pciexrc creation is done, or the di_node is a pcibus, in either
3910942STom.Pothier@Sun.COM  * case the pcibus module will loaded to enumerate pciexbus/pcibus etc.
4010942STom.Pothier@Sun.COM  *
4110942STom.Pothier@Sun.COM  * The enumeration uses did routines heavily, which requires a did hash
4210942STom.Pothier@Sun.COM  * pointer stored in x86pi's module-specific area.
4310942STom.Pothier@Sun.COM  */
4410942STom.Pothier@Sun.COM 
4510942STom.Pothier@Sun.COM #include <sys/types.h>
4610942STom.Pothier@Sun.COM #include <strings.h>
4710942STom.Pothier@Sun.COM #include <fm/topo_mod.h>
4810942STom.Pothier@Sun.COM #include <fm/topo_hc.h>
4910942STom.Pothier@Sun.COM #include <sys/systeminfo.h>
5010942STom.Pothier@Sun.COM #include <sys/smbios_impl.h>
5110942STom.Pothier@Sun.COM #include <sys/fm/protocol.h>
5210942STom.Pothier@Sun.COM #include <x86pi_impl.h>
5310942STom.Pothier@Sun.COM #include <did.h>
5410942STom.Pothier@Sun.COM #include <did_impl.h>
5510942STom.Pothier@Sun.COM #include <did_props.h>
5610942STom.Pothier@Sun.COM #include <hostbridge.h>
5710942STom.Pothier@Sun.COM 
5810942STom.Pothier@Sun.COM #define	PCI_ENUM	"pcibus"
5910942STom.Pothier@Sun.COM #define	PCI_ENUMR_VERS	1
6010942STom.Pothier@Sun.COM #define	MAX_HB_BUSES	255
6110942STom.Pothier@Sun.COM 
6210942STom.Pothier@Sun.COM extern txprop_t RC_common_props[], HB_common_props[], ExHB_common_props[];
6310942STom.Pothier@Sun.COM extern int RC_propcnt, HB_propcnt, ExHB_propcnt;
6410942STom.Pothier@Sun.COM 
6510942STom.Pothier@Sun.COM static topo_mod_t *pcimp = NULL;
6610942STom.Pothier@Sun.COM 
6710942STom.Pothier@Sun.COM int
x86pi_hbr_enum_init(topo_mod_t * mod)6810942STom.Pothier@Sun.COM x86pi_hbr_enum_init(topo_mod_t *mod)
6910942STom.Pothier@Sun.COM {
7010942STom.Pothier@Sun.COM 	const char *f = "x86pi_hbr_enum_init";
7110942STom.Pothier@Sun.COM 
7212355SSean.Ye@Sun.COM 	if (did_hash_init(mod) < 0) {
7310942STom.Pothier@Sun.COM 		topo_mod_dprintf(mod, "%s: did_hash_init() failed.\n", f);
7410942STom.Pothier@Sun.COM 		return (-1);
7510942STom.Pothier@Sun.COM 	}
7610942STom.Pothier@Sun.COM 
7712355SSean.Ye@Sun.COM 	if ((pcimp = topo_mod_load(mod, PCI_ENUM, PCI_ENUMR_VERS)) == NULL) {
7810942STom.Pothier@Sun.COM 		topo_mod_dprintf(mod,
7910942STom.Pothier@Sun.COM 		    "%s: %s enumerator could not load %s.\n",
8010942STom.Pothier@Sun.COM 		    f, HOSTBRIDGE, PCI_ENUM);
8110942STom.Pothier@Sun.COM 		did_hash_fini(mod);
8210942STom.Pothier@Sun.COM 		return (-1);
8310942STom.Pothier@Sun.COM 	}
8410942STom.Pothier@Sun.COM 
8510942STom.Pothier@Sun.COM 	return (0);
8610942STom.Pothier@Sun.COM }
8710942STom.Pothier@Sun.COM 
8810942STom.Pothier@Sun.COM void
x86pi_hbr_enum_fini(topo_mod_t * mod)8910942STom.Pothier@Sun.COM x86pi_hbr_enum_fini(topo_mod_t *mod)
9010942STom.Pothier@Sun.COM {
9110942STom.Pothier@Sun.COM 	did_hash_fini(mod);
9212355SSean.Ye@Sun.COM 	topo_mod_unload(pcimp);
9312355SSean.Ye@Sun.COM 	pcimp = NULL;
9410942STom.Pothier@Sun.COM }
9510942STom.Pothier@Sun.COM 
9610942STom.Pothier@Sun.COM static int
pciex_process(topo_mod_t * mod,tnode_t * tn_hbr,di_node_t rcn,topo_instance_t rci)9710942STom.Pothier@Sun.COM pciex_process(topo_mod_t *mod, tnode_t *tn_hbr, di_node_t rcn,
9810942STom.Pothier@Sun.COM     topo_instance_t rci)
9910942STom.Pothier@Sun.COM {
10010942STom.Pothier@Sun.COM 	did_t		*did;
10110942STom.Pothier@Sun.COM 	int		rv;
10210942STom.Pothier@Sun.COM 	tnode_t		*tn_rc;
10310942STom.Pothier@Sun.COM 	x86pi_hcfmri_t	hcfmri = {0};
10410942STom.Pothier@Sun.COM 	tnode_t		*tn_bb = topo_node_parent(tn_hbr);
10510942STom.Pothier@Sun.COM 	const char	*f = "pciexrc_process";
10610942STom.Pothier@Sun.COM 
10710942STom.Pothier@Sun.COM 	if ((did = did_create(mod, rcn, topo_node_instance(tn_bb),
10810942STom.Pothier@Sun.COM 	    topo_node_instance(tn_hbr), rci, TRUST_BDF)) == NULL)
10910942STom.Pothier@Sun.COM 		return (NULL);
11010942STom.Pothier@Sun.COM 
11110942STom.Pothier@Sun.COM 	did_markrc(did);
11210942STom.Pothier@Sun.COM 
11310942STom.Pothier@Sun.COM 	/*
11410942STom.Pothier@Sun.COM 	 * Let did set the hostbridge properties excluding FRU and label.
11510942STom.Pothier@Sun.COM 	 */
11610942STom.Pothier@Sun.COM 	(void) did_props_set(tn_hbr, did, ExHB_common_props, ExHB_propcnt - 2);
11710942STom.Pothier@Sun.COM 
11810942STom.Pothier@Sun.COM 	if (topo_node_range_create(mod, tn_hbr, PCIEX_ROOT, 0,
11910942STom.Pothier@Sun.COM 	    MAX_HB_BUSES) != 0 && topo_mod_errno(mod) != EMOD_NODE_DUP) {
12010942STom.Pothier@Sun.COM 		topo_mod_dprintf(mod,
12110942STom.Pothier@Sun.COM 		    "%s: create child range for %s failed: %s\n",
12210942STom.Pothier@Sun.COM 		    f, PCIEX_ROOT, topo_mod_errmsg(mod));
12310942STom.Pothier@Sun.COM 		return (-1);
12410942STom.Pothier@Sun.COM 	}
12510942STom.Pothier@Sun.COM 
12610942STom.Pothier@Sun.COM 	hcfmri.hc_name = PCIEX_ROOT;
12710942STom.Pothier@Sun.COM 	hcfmri.instance = rci;
12810942STom.Pothier@Sun.COM 	rv = x86pi_enum_generic(mod, &hcfmri, tn_hbr, tn_hbr, &tn_rc, 0);
12910942STom.Pothier@Sun.COM 	if (rv != 0) {
13010942STom.Pothier@Sun.COM 		topo_mod_dprintf(mod, "%s: failed to create %s = %d\n",
13110942STom.Pothier@Sun.COM 		    f, PCIEX_ROOT, rci);
13210942STom.Pothier@Sun.COM 		return (-1);
13310942STom.Pothier@Sun.COM 	}
13410942STom.Pothier@Sun.COM 
13510942STom.Pothier@Sun.COM 	/*
13610942STom.Pothier@Sun.COM 	 * pcibus enumerator requires di_node_t be set in node specific
13710942STom.Pothier@Sun.COM 	 */
13810942STom.Pothier@Sun.COM 	topo_node_setspecific(tn_rc, rcn);
13910942STom.Pothier@Sun.COM 
14010942STom.Pothier@Sun.COM 	/*
14110942STom.Pothier@Sun.COM 	 * Let did set the RC properties excluding FRU, and label.
14210942STom.Pothier@Sun.COM 	 */
14310942STom.Pothier@Sun.COM 	if (did_props_set(tn_rc, did, RC_common_props, RC_propcnt - 2) < 0) {
14410942STom.Pothier@Sun.COM 		topo_mod_dprintf(mod, "%s: did_props_set failed for %s = %d\n",
14510942STom.Pothier@Sun.COM 		    f, PCIEX_ROOT, rci);
14610942STom.Pothier@Sun.COM 		topo_node_unbind(tn_rc);
14710942STom.Pothier@Sun.COM 		return (-1);
14810942STom.Pothier@Sun.COM 	}
14910942STom.Pothier@Sun.COM 
15010942STom.Pothier@Sun.COM 	if (topo_node_range_create(mod, tn_rc, PCIEX_BUS, 0,
15110942STom.Pothier@Sun.COM 	    MAX_HB_BUSES) != 0 && topo_mod_errno(mod) != EMOD_NODE_DUP) {
15210942STom.Pothier@Sun.COM 		topo_mod_dprintf(mod,
15310942STom.Pothier@Sun.COM 		    "%s: create child range for %s failed: %s\n",
15410942STom.Pothier@Sun.COM 		    f, PCIEX_BUS, topo_mod_errmsg(mod));
15510942STom.Pothier@Sun.COM 		return (-1);
15610942STom.Pothier@Sun.COM 	}
15710942STom.Pothier@Sun.COM 
15810942STom.Pothier@Sun.COM 	return (topo_mod_enumerate(mod, tn_rc, PCI_BUS, PCIEX_BUS,
15910942STom.Pothier@Sun.COM 	    0, MAX_HB_BUSES, did));
16010942STom.Pothier@Sun.COM }
16110942STom.Pothier@Sun.COM 
16210942STom.Pothier@Sun.COM static int
pci_process(topo_mod_t * mod,tnode_t * tn_hbr,di_node_t bn)16310942STom.Pothier@Sun.COM pci_process(topo_mod_t *mod, tnode_t *tn_hbr, di_node_t bn)
16410942STom.Pothier@Sun.COM {
16510942STom.Pothier@Sun.COM 	did_t *did;
16610942STom.Pothier@Sun.COM 	tnode_t *tn_bb = topo_node_parent(tn_hbr);
16710942STom.Pothier@Sun.COM 
16810942STom.Pothier@Sun.COM 	if ((did = did_create(mod, bn, topo_node_instance(tn_bb),
16910942STom.Pothier@Sun.COM 	    topo_node_instance(tn_hbr), NO_RC, TRUST_BDF)) == NULL)
17010942STom.Pothier@Sun.COM 		return (-1);
17110942STom.Pothier@Sun.COM 
17210942STom.Pothier@Sun.COM 	/*
17310942STom.Pothier@Sun.COM 	 * Let did set the hostbridge properties excluding FRU and label.
17410942STom.Pothier@Sun.COM 	 */
17510942STom.Pothier@Sun.COM 	(void) did_props_set(tn_hbr, did, HB_common_props, HB_propcnt - 2);
17610942STom.Pothier@Sun.COM 
17710942STom.Pothier@Sun.COM 	if (topo_node_range_create(mod, tn_hbr, PCI_BUS, 0,
17810942STom.Pothier@Sun.COM 	    MAX_HB_BUSES) != 0 && topo_mod_errno(mod) != EMOD_NODE_DUP) {
17910942STom.Pothier@Sun.COM 		topo_mod_dprintf(mod, "create child range for %s failed: %s\n",
18010942STom.Pothier@Sun.COM 		    PCI_BUS, topo_mod_errmsg(mod));
18110942STom.Pothier@Sun.COM 		return (-1);
18210942STom.Pothier@Sun.COM 	}
18310942STom.Pothier@Sun.COM 
18410942STom.Pothier@Sun.COM 	return (topo_mod_enumerate(mod, tn_hbr, PCI_BUS, PCI_BUS,
18510942STom.Pothier@Sun.COM 	    0, MAX_HB_BUSES, did));
18610942STom.Pothier@Sun.COM }
18710942STom.Pothier@Sun.COM 
18810942STom.Pothier@Sun.COM static int
x86pi_gen_pci_pciexrc(topo_mod_t * mod,tnode_t * tn_hbr,uint16_t bdf,topo_instance_t * rcip)18910942STom.Pothier@Sun.COM x86pi_gen_pci_pciexrc(topo_mod_t *mod, tnode_t *tn_hbr, uint16_t bdf,
19010942STom.Pothier@Sun.COM     topo_instance_t *rcip)
19110942STom.Pothier@Sun.COM {
19210942STom.Pothier@Sun.COM 	di_node_t devtree, pnode, cnode;
19310942STom.Pothier@Sun.COM 
19410942STom.Pothier@Sun.COM 	topo_mod_dprintf(mod, "creating pci/pciexrc node bdf = %#x\n",
19510942STom.Pothier@Sun.COM 	    (int)bdf);
19610942STom.Pothier@Sun.COM 
19710942STom.Pothier@Sun.COM 	devtree = topo_mod_devinfo(mod);
19810942STom.Pothier@Sun.COM 	if (devtree == DI_NODE_NIL) {
19910942STom.Pothier@Sun.COM 		topo_mod_dprintf(mod, "devinfo init failed.\n");
20010942STom.Pothier@Sun.COM 		return (-1);
20110942STom.Pothier@Sun.COM 	}
20210942STom.Pothier@Sun.COM 
20310942STom.Pothier@Sun.COM 	for (pnode = di_drv_first_node(PCI, devtree);
20410942STom.Pothier@Sun.COM 	    pnode != DI_NODE_NIL; pnode = di_drv_next_node(pnode))
20510942STom.Pothier@Sun.COM 		if (x86pi_bdf(mod, pnode) == bdf)
20610942STom.Pothier@Sun.COM 			return (pci_process(mod, tn_hbr, pnode));
20710942STom.Pothier@Sun.COM 
20810942STom.Pothier@Sun.COM 	pnode = di_drv_first_node(NPE, devtree);
20910942STom.Pothier@Sun.COM 	while (pnode != DI_NODE_NIL) {
21010942STom.Pothier@Sun.COM 		for (cnode = di_child_node(pnode); cnode != DI_NODE_NIL;
21110942STom.Pothier@Sun.COM 		    cnode = di_sibling_node(cnode)) {
21210942STom.Pothier@Sun.COM 			if (di_driver_name(cnode) == NULL ||
21310942STom.Pothier@Sun.COM 			    x86pi_bdf(mod, cnode) != bdf)
21410942STom.Pothier@Sun.COM 				continue;
21510942STom.Pothier@Sun.COM 
21610942STom.Pothier@Sun.COM 			if (strcmp(di_driver_name(cnode), PCI_PCI) == 0)
21710942STom.Pothier@Sun.COM 				return (pci_process(mod, tn_hbr, cnode));
21810942STom.Pothier@Sun.COM 
21910942STom.Pothier@Sun.COM 			if (strcmp(di_driver_name(cnode), PCIEB) == 0)
22010942STom.Pothier@Sun.COM 				return (pciex_process(mod, tn_hbr,
22110942STom.Pothier@Sun.COM 				    cnode, (*rcip)++));
22210942STom.Pothier@Sun.COM 
22310942STom.Pothier@Sun.COM 			topo_mod_dprintf(mod, "no matching driver found: "
22410942STom.Pothier@Sun.COM 			    "bdf = %#x\n", (int)bdf);
22510942STom.Pothier@Sun.COM 		}
22610942STom.Pothier@Sun.COM 		pnode = di_drv_next_node(pnode);
22710942STom.Pothier@Sun.COM 	}
22810942STom.Pothier@Sun.COM 
22910942STom.Pothier@Sun.COM 	topo_mod_dprintf(mod, "no matching bdf found: bdf = %#x\n", (int)bdf);
23010942STom.Pothier@Sun.COM 
23110942STom.Pothier@Sun.COM 	return (0);
23210942STom.Pothier@Sun.COM }
23310942STom.Pothier@Sun.COM 
23410942STom.Pothier@Sun.COM int
x86pi_gen_hbr(topo_mod_t * mod,tnode_t * tn_bb,int hbr_smbid,topo_instance_t hbri,topo_instance_t * rcip)235*12832STrang.Do@Sun.COM x86pi_gen_hbr(topo_mod_t *mod, tnode_t *tn_bb,
23610942STom.Pothier@Sun.COM     int hbr_smbid, topo_instance_t hbri, topo_instance_t *rcip)
23710942STom.Pothier@Sun.COM {
23810942STom.Pothier@Sun.COM 	x86pi_hcfmri_t	hcfmri = {0};
23910942STom.Pothier@Sun.COM 	tnode_t		*tn_hbr;
24010942STom.Pothier@Sun.COM 	smbs_cnt_t	*smbc = &stypes[SUN_OEM_PCIEXRC];
24110942STom.Pothier@Sun.COM 	smbios_pciexrc_t smb_rc;
24210942STom.Pothier@Sun.COM 	int		i, rv, err = 0;
24310942STom.Pothier@Sun.COM 	const char	*f = "x86pi_gen_hbr";
244*12832STrang.Do@Sun.COM 	smbios_hdl_t 	*shp;
245*12832STrang.Do@Sun.COM 
246*12832STrang.Do@Sun.COM 	shp = topo_mod_smbios(mod);
247*12832STrang.Do@Sun.COM 	if (shp == NULL)
248*12832STrang.Do@Sun.COM 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
24910942STom.Pothier@Sun.COM 
25010942STom.Pothier@Sun.COM 	hcfmri.hc_name = HOSTBRIDGE;
25110942STom.Pothier@Sun.COM 	hcfmri.instance = hbri;
25210942STom.Pothier@Sun.COM 
25310942STom.Pothier@Sun.COM 	/* create and bind the "hostbridge" node */
25410942STom.Pothier@Sun.COM 	rv = x86pi_enum_generic(mod, &hcfmri, tn_bb, tn_bb, &tn_hbr, 0);
25510942STom.Pothier@Sun.COM 	if (rv != 0) {
25610942STom.Pothier@Sun.COM 		topo_mod_dprintf(mod, "%s: failed to create %s = %d\n",
25710942STom.Pothier@Sun.COM 		    f, HOSTBRIDGE, hbri);
25810942STom.Pothier@Sun.COM 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
25910942STom.Pothier@Sun.COM 	}
26010942STom.Pothier@Sun.COM 
26110942STom.Pothier@Sun.COM 	/*
26210942STom.Pothier@Sun.COM 	 * Walk the smbios records and create the pci/pciexrc nodes
26310942STom.Pothier@Sun.COM 	 */
26410942STom.Pothier@Sun.COM 	for (i = 0; i < smbc->count; i++) {
26510942STom.Pothier@Sun.COM 		if (smbios_info_pciexrc(shp, smbc->ids[i].id, &smb_rc) != 0)
26610942STom.Pothier@Sun.COM 			topo_mod_dprintf(mod,
26710942STom.Pothier@Sun.COM 			    "%s: failed: id = %d\n", f, (int)smbc->ids[i].id);
26810942STom.Pothier@Sun.COM 		else if (smb_rc.smbpcie_bb == hbr_smbid &&
26910942STom.Pothier@Sun.COM 		    x86pi_gen_pci_pciexrc(mod, tn_hbr, smb_rc.smbpcie_bdf,
27010942STom.Pothier@Sun.COM 		    rcip) != 0)
27110942STom.Pothier@Sun.COM 			err++;
27210942STom.Pothier@Sun.COM 	}
27310942STom.Pothier@Sun.COM 
27410942STom.Pothier@Sun.COM 	return (err == 0 ? 0 : topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
27510942STom.Pothier@Sun.COM }
276