11414Scindi /*
21414Scindi * CDDL HEADER START
31414Scindi *
41414Scindi * The contents of this file are subject to the terms of the
51414Scindi * Common Development and Distribution License (the "License").
61414Scindi * You may not use this file except in compliance with the License.
71414Scindi *
81414Scindi * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91414Scindi * or http://www.opensolaris.org/os/licensing.
101414Scindi * See the License for the specific language governing permissions
111414Scindi * and limitations under the License.
121414Scindi *
131414Scindi * When distributing Covered Code, include this CDDL HEADER in each
141414Scindi * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151414Scindi * If applicable, add the following below this CDDL HEADER, with the
161414Scindi * fields enclosed by brackets "[]" replaced with your own identifying
171414Scindi * information: Portions Copyright [yyyy] [name of copyright owner]
181414Scindi *
191414Scindi * CDDL HEADER END
201414Scindi */
211414Scindi
221414Scindi /*
231414Scindi * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
241414Scindi * Use is subject to license terms.
251414Scindi */
261414Scindi
271414Scindi #pragma ident "%Z%%M% %I% %E% SMI"
281414Scindi
29*3062Scindi #include <fm/topo_hc.h>
30*3062Scindi
31*3062Scindi #include <hb_sun4.h>
32*3062Scindi #include <hostbridge.h>
33*3062Scindi #include <pcibus.h>
34*3062Scindi #include <util.h>
351414Scindi
361414Scindi int
count_busorrc(topo_mod_t * mod,busorrc_t * list,int * hbc,int * bph)37*3062Scindi count_busorrc(topo_mod_t *mod, busorrc_t *list, int *hbc, int *bph)
381414Scindi {
391414Scindi ulong_t start;
401414Scindi busorrc_t *p;
411414Scindi int bt;
421414Scindi
431414Scindi start = list->br_ba_ac;
441414Scindi p = list->br_nextbus;
451414Scindi bt = *hbc = 1;
461414Scindi while (p != NULL) {
471414Scindi if (p->br_ba_ac == start)
481414Scindi (*hbc)++;
491414Scindi bt++;
501414Scindi p = p->br_nextbus;
511414Scindi }
521414Scindi
531414Scindi /*
541414Scindi * sanity check that we have the correct number of buses/root
551414Scindi * complexes in the list to have the same number of buses on
561414Scindi * each hostbridge
571414Scindi */
581414Scindi if (bt % *hbc != 0) {
592027Ssethg topo_mod_dprintf(mod,
601414Scindi "Imbalance between bus/root complex count and "
611414Scindi "the number of hostbridges.\n");
622027Ssethg return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
631414Scindi }
641414Scindi *bph = bt / *hbc;
652027Ssethg topo_mod_dprintf(mod,
661414Scindi "%d hostbridge%s\n", *hbc, (*hbc > 1) ? "s." : ".");
672027Ssethg topo_mod_dprintf(mod, "%d buses total.\n", bt);
681414Scindi return (0);
691414Scindi }
701414Scindi
711414Scindi static int
busorrc_process(topo_mod_t * mod,busorrc_t * list,int isrc,tnode_t * ptn)72*3062Scindi busorrc_process(topo_mod_t *mod, busorrc_t *list, int isrc, tnode_t *ptn)
731414Scindi {
741414Scindi int hbc, busper;
751414Scindi
761414Scindi if (list == NULL) {
771414Scindi if (isrc == 1)
782027Ssethg topo_mod_dprintf(mod, "No root complexes found.\n");
791414Scindi else
802027Ssethg topo_mod_dprintf(mod, "No pci buses found.\n");
811414Scindi return (0);
821414Scindi }
831414Scindi
841414Scindi /*
851414Scindi * At this point we've looked through all the top-level device
861414Scindi * tree nodes for instances of drivers that represent logical
871414Scindi * PCI buses or root complexes. We've sorted them into a
881414Scindi * list, ordered by "bus address". We retrieved "bus address"
891414Scindi * using di_bus_addr(). That gave us a string that contains
901414Scindi * either a single hex number or a pair of them separated by a
911414Scindi * comma. If there was a single number, we've assumed the
921414Scindi * second number to be zero.
931414Scindi *
941414Scindi * So, we always have a pair of numbers describing a bus/root
951414Scindi * complex, X1 and X2, with X1 being the number before the
961414Scindi * comma, and X2 being the number after (or the assumed zero).
971414Scindi * As each node was examined, we then sorted these buses/root
981414Scindi * complexes, first by the value of X2, and using X1 to order
991414Scindi * amongst buses/root complexes with the same value for X2.
1001414Scindi *
1011414Scindi * We infer the existence of hostbridges by observing a
1021414Scindi * pattern that X2 is recycled for different hostbridges, and
1031414Scindi * that sorting by X1 within buses/root complexes with equal
1041414Scindi * values of X2 maintains the correct associations of
1051414Scindi * buses/root complexes and bridges.
1061414Scindi */
107*3062Scindi if (count_busorrc(mod, list, &hbc, &busper) < 0)
1081414Scindi return (-1);
1091414Scindi if (isrc == 1)
110*3062Scindi return (declare_exbuses(mod, list, ptn, hbc, busper));
1111414Scindi else
112*3062Scindi return (declare_buses(mod, list, ptn, hbc));
1131414Scindi }
1141414Scindi
1151414Scindi static int
pci_hostbridges_find(topo_mod_t * mod,tnode_t * ptn)116*3062Scindi pci_hostbridges_find(topo_mod_t *mod, tnode_t *ptn)
1171414Scindi {
1181414Scindi busorrc_t *buses = NULL;
1191414Scindi busorrc_t *rcs = NULL;
1201414Scindi di_node_t devtree;
1211414Scindi di_node_t pnode;
1221414Scindi
1231414Scindi /* Scan for buses, top-level devinfo nodes with the right driver */
124*3062Scindi devtree = topo_mod_devinfo(mod);
1251414Scindi if (devtree == DI_NODE_NIL) {
1262027Ssethg topo_mod_dprintf(mod, "devinfo init failed.");
1271414Scindi topo_node_range_destroy(ptn, HOSTBRIDGE);
1282027Ssethg return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
1291414Scindi }
1301414Scindi
1311414Scindi pnode = di_drv_first_node(PCI, devtree);
1321414Scindi while (pnode != DI_NODE_NIL) {
133*3062Scindi if (busorrc_add(mod, &buses, pnode) < 0) {
1342027Ssethg return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
1351414Scindi }
1361414Scindi pnode = di_drv_next_node(pnode);
1371414Scindi }
1381414Scindi pnode = di_drv_first_node(PSYCHO, devtree);
1391414Scindi while (pnode != DI_NODE_NIL) {
140*3062Scindi if (busorrc_add(mod, &buses, pnode) < 0) {
1412027Ssethg return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
1421414Scindi }
1431414Scindi pnode = di_drv_next_node(pnode);
1441414Scindi }
1451414Scindi pnode = di_drv_first_node(SCHIZO, devtree);
1461414Scindi while (pnode != DI_NODE_NIL) {
147*3062Scindi if (busorrc_add(mod, &buses, pnode) < 0) {
1482027Ssethg return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
1491414Scindi }
1501414Scindi pnode = di_drv_next_node(pnode);
1511414Scindi }
1521414Scindi pnode = di_drv_first_node(PX, devtree);
1531414Scindi while (pnode != DI_NODE_NIL) {
154*3062Scindi if (busorrc_add(mod, &rcs, pnode) < 0) {
1552027Ssethg return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
1561414Scindi }
1571414Scindi pnode = di_drv_next_node(pnode);
1581414Scindi }
159*3062Scindi if (busorrc_process(mod, buses, 0, ptn) < 0)
1602027Ssethg return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
1611414Scindi
162*3062Scindi if (busorrc_process(mod, rcs, 1, ptn) < 0)
1632027Ssethg return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
1641414Scindi
165*3062Scindi busorrc_free(mod, buses);
166*3062Scindi busorrc_free(mod, rcs);
1671414Scindi return (0);
1681414Scindi }
1691414Scindi
1701414Scindi /*ARGSUSED*/
1711414Scindi int
platform_hb_enum(topo_mod_t * mod,tnode_t * parent,const char * name,topo_instance_t imin,topo_instance_t imax)172*3062Scindi platform_hb_enum(topo_mod_t *mod, tnode_t *parent, const char *name,
173*3062Scindi topo_instance_t imin, topo_instance_t imax)
1741414Scindi {
175*3062Scindi return (pci_hostbridges_find(mod, parent));
1761414Scindi }
1771414Scindi
1781414Scindi /*ARGSUSED*/
1791414Scindi int
platform_hb_label(topo_mod_t * mod,tnode_t * node,nvlist_t * in,nvlist_t ** out)180*3062Scindi platform_hb_label(topo_mod_t *mod, tnode_t *node, nvlist_t *in, nvlist_t **out)
1811414Scindi {
1822027Ssethg return (labelmethod_inherit(mod, node, in, out));
1831414Scindi }
184