OpenSolaris_b135/lib/libprtdiag_psr/sparc/daktari/common/workfile.c

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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 2000, 2003 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 *
 * Daktari Platform specific functions.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <libdevinfo.h>
#include <alloca.h>
#include <inttypes.h>
#include <libprtdiag.h>
#include <sys/mc.h>

#define	EXIT_MSG(msg, err) \
	{ printf("\n%s failed with %d\n", msg, err); exit(err); }

/* we only need the 5 LSB of the portid to calculate the board number */
#define	DAK_SAFARI_ID_MASK		0x1F	/* 5 bits */
#define	DAK_NODE_MASK			0x1F	/* 5 bits */
#define	DAK_PORTID_NODE_SHIFT		5
#define	DAK_MIN_CPU_SAFARI_ID		0	/* 0x00 */
#define	DAK_MAX_CPU_SAFARI_ID		23	/* 0x17 */
#define	DAK_MIN_IO_SAFARI_ID		24	/* 0x18 */
#define	DAK_MAX_IO_SAFARI_ID		31	/* 0x1F */
#define	NUM_MBANKS_PER_MC		4

#define	DAK_CLK_FREQ_TO_MHZ(x)	(((x) + 500000) / 1000000)

/*
 * DAK_PORTID_IS_CPU_TYPE
 *
 * If the portid associated with a CPU board is passed in, TRUE is returned,
 * otherwise FALSE.
 */
#define	DAK_PORTID_IS_CPU_TYPE(portid) \
	(((((portid) & DAK_SAFARI_ID_MASK) >= DAK_MIN_CPU_SAFARI_ID) && \
	(((portid) & DAK_SAFARI_ID_MASK) <= DAK_MAX_CPU_SAFARI_ID)) ? \
								TRUE: FALSE)
/*
 * DAK_CPU_BD_PORTID_TO_BD_NUM
 *
 * If the portid associated with a CPU board is passed in, the board number
 * associated with this portid is returned, otherwise -1.
 */
#define	DAK_CPU_BD_PORTID_TO_BD_NUM(portid) \
	((DAK_PORTID_IS_CPU_TYPE(portid)) ? \
		(((portid) & DAK_SAFARI_ID_MASK) / 4) : (-1))

/*
 * DAK_PORTID_IS_IO_TYPE
 *
 * If the portid associated with an IO board is passed in, TRUE is returned,
 * otherwise FALSE.
 */
#define	DAK_PORTID_IS_IO_TYPE(portid) \
	(((((portid) & DAK_SAFARI_ID_MASK) >= DAK_MIN_IO_SAFARI_ID) && \
	(((portid) & DAK_SAFARI_ID_MASK) <= DAK_MAX_IO_SAFARI_ID)) ? \
								TRUE: FALSE)

/*
 * DAK_IO_BD_PORTID_TO_BD_NUM
 *
 * If the portid associated with an IO board is passed in, the board number
 * associated with this portid is returned, otherwise -1.
 */
#define	DAK_IO_BD_PORTID_TO_BD_NUM(portid) \
	(DAK_PORTID_IS_IO_TYPE(portid) ? \
		(((((portid) & DAK_SAFARI_ID_MASK) - 24) / 2) + 6) : (-1))

/*
 * DAK_PORTID_TO_BOARD_NUM
 *
 * If a valid portid is passed in, this macro returns the board number
 * associated with it, otherwise it returns -1.
 */

#define	DAK_PORTID_TO_BOARD_NUM(portid) \
	((DAK_PORTID_IS_CPU_TYPE(portid)) ? \
		(DAK_CPU_BD_PORTID_TO_BD_NUM(portid)) : \
	((DAK_PORTID_IS_IO_TYPE(portid)) ? \
		DAK_IO_BD_PORTID_TO_BD_NUM(portid) : (-1)))


/* Local Functions */
char	*get_node_name(Prom_node *pnode);
char	*get_node_type(Prom_node *pnode);
void	 add_node(Sys_tree *root, Prom_node *pnode);
Prop	*find_prop(Prom_node *pnode, char *name);
int	 do_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag);
void	*get_prop_val(Prop *prop);
char    *get_node_type(Prom_node *pnode);
int	 get_us3_mem_regs(Board_node *bnode);

void	 fill_pci_card_list(Prom_node *pci_instance,
			    Prom_node *pci_card_node,
			    struct io_card *pci_card,
			    struct io_card **pci_card_list,
			    char **pci_slot_name_arr);

static Prom_node	*next_pci_card(Prom_node *curr_card, int *is_bridge,
				int is_pcidev, Prom_node *curr_bridge,
				Prom_node * parent_bridge, Prom_node *pci);

static	Prom_node *dev_next_node_by_compat(Prom_node *root, char *compat);
static	Prom_node *dev_find_node_by_compat(Prom_node *root, char *compat);
static Board_node *daktari_insert_board(Sys_tree *root, int board);
static Board_node *daktari_find_board(Sys_tree *root, int board);

static    int32_t find_child_device(picl_nodehdl_t, char *, picl_nodehdl_t *);
static    int32_t fill_device_from_id(picl_nodehdl_t, char *, picl_nodehdl_t *);
static    int32_t fill_device_array_from_id(picl_nodehdl_t, char *, int32_t *,
				picl_nodehdl_t **);

/* Overlaying routines */

/*
 * This function searches through the properties of the node passed in
 * and returns a pointer to the value of the name property.
 */
char *
get_node_name(Prom_node *pnode)
{
	Prop *prop;

	if (pnode == NULL)
	    return (NULL);

	prop = pnode->props;
	while (prop != NULL) {
		if (strcmp("name", (char *)prop->name.val_ptr) == 0)
			return (prop->value.val_ptr);
		prop = prop->next;
	}
	return (NULL);
}

/*
 * This function searches through the properties of the node passed in
 * and returns a pointer to the value of the name property.
 */
char *
get_node_type(Prom_node *pnode)
{
	Prop	*prop;

	if (pnode == NULL) {
		return (NULL);
	}

	prop = pnode->props;
	while (prop != NULL) {
		if (strcmp("device_type", (char *)prop->name.val_ptr) == 0)
			return (prop->value.val_ptr);
		prop = prop->next;
	}
	return (NULL);
}

/*
 * add_node
 *
 * This function adds a board node to the board structure where that
 * that node's physical component lives.
 */
void
add_node(Sys_tree *root, Prom_node *pnode)
{
	int		board	= -1;
	int		portid	= -1;

	void		*value	= NULL;
	Board_node	*bnode	= NULL;
	Prom_node	*p	= NULL;

	/* Get the board number of this board from the portid prop */
	value = get_prop_val(find_prop(pnode, "portid"));
	if (value != NULL) {
		portid = *(int *)value;
	}
	board	= DAK_PORTID_TO_BOARD_NUM(portid);
	/*	board = DAK_GETSLOT(portid); */
	/* find the board node with the same board number */
	if ((bnode = daktari_find_board(root, board)) == NULL) {
		bnode = daktari_insert_board(root, board);
	}

	/* now attach this prom node to the board list */
	/* Insert this node at the end of the list */
	pnode->sibling = NULL;
	if (bnode->nodes == NULL)
		bnode->nodes = pnode;
	else {
		p = bnode->nodes;
		while (p->sibling != NULL)
			p = p->sibling;
		p->sibling = pnode;
	}
}

/*
 * Search a Prom node and retrieve the property with the correct
 * name.
 */
Prop *
find_prop(Prom_node *pnode, char *name)
{
	Prop	*prop;

	if (pnode  == NULL)
	    return (NULL);

	if (pnode->props == NULL)
	    return (NULL);

	prop = pnode->props;

	if (prop == NULL)
	    return (NULL);

	if (prop->name.val_ptr == NULL)
	    return (NULL);

	while ((prop != NULL) && (strcmp((char *)(prop->name.val_ptr), name))) {
		prop = prop->next;
	}
	return (prop);
}

int
do_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag)
{
	return (do_devinfo(syserrlog, pgname, log_flag, prt_flag));
}


/*
 * return the property value for the Prop
 * passed in.
 */
void *
get_prop_val(Prop *prop)
{
	if (prop == NULL)
	    return (NULL);

	return ((void *)(prop->value.val_ptr));
}

/* Local Routines */

/*
 * Start from the current node and return the next node besides
 * the current one which has the requested model property.
 */
static Prom_node *
dev_next_node_by_compat(Prom_node *root, char *compat)
{
	Prom_node *node;

	if (root == NULL)
	    return (NULL);

	/* look at your children first */
	if ((node = dev_find_node_by_compat(root->child, compat)) != NULL)
	    return (node);

	/* now look at your siblings */
	if ((node = dev_find_node_by_compat(root->sibling, compat)) != NULL)
	    return (node);

	return (NULL);  /* not found */
}

/*
 * Do a depth-first walk of a device tree and
 * return the first node with the matching model.
 */
static Prom_node *
dev_find_node_by_compat(Prom_node *root, char *compat)
{
	Prom_node	*node;
	char		*compatible;
	char		*name;

	if (root == NULL)
		return (NULL);

	if (compat == NULL)
		return (NULL);

	name = get_node_name(root);
	if (name == NULL)
		name = "";

	compatible = (char *)get_prop_val(find_prop(root, "compatible"));

	if (compatible == NULL)
	    return (NULL);

	if ((strcmp(name, "pci") == 0) && (compatible != NULL) &&
	    (strcmp(compatible, compat) == 0)) {
		return (root); /* found a match */
	}

	/* look at your children first */
	if ((node = dev_find_node_by_compat(root->child, compat)) != NULL)
	    return (node);

	/* now look at your siblings */
	if ((node = dev_find_node_by_compat(root->sibling, compat)) != NULL)
	    return (node);

	return (NULL);  /* not found */
}


/*
 * Add a board to the system list in order (sorted by board#).
 * Initialize all pointer fields to NULL.
 */
static Board_node *
daktari_insert_board(Sys_tree *root, int board)
{
	Board_node	*bnode;
	Board_node	*temp = root->bd_list;

	if ((bnode = (Board_node *) malloc(sizeof (Board_node))) == NULL) {
		perror("malloc");
		exit(1);
	}

	bnode->nodes		= NULL;
	bnode->next		= NULL;
	bnode->board_num	= board;
	bnode->board_type	= UNKNOWN_BOARD;

	if (temp == NULL)
	    root->bd_list = bnode;

	else if (temp->board_num > board) {
		bnode->next = temp;
		root->bd_list = bnode;

	} else {
		while ((temp->next != NULL) && (board > temp->next->board_num))
			temp = temp->next;

		bnode->next = temp->next;
		temp->next = bnode;
	}
	root->board_cnt++;

	return (bnode);
}

/*
 * Find the requested board struct in the system device tree.
 *
 * This function overrides the functionality of the generic find_board()
 * function in libprtdiag, but since we need to pass another parameter,
 * we cannot simply overlay the symbol table.
 */
static Board_node *
daktari_find_board(Sys_tree *root, int board)
{
	Board_node *bnode = root->bd_list;

	while ((bnode != NULL) && (board != bnode->board_num)) {
		bnode = bnode->next;
	}
	return (bnode);
}


int32_t
find_child_device(picl_nodehdl_t parent, char *child_name,
		picl_nodehdl_t *child)
{
	int32_t		err;
	char		name[PICL_PROPNAMELEN_MAX];

	err = picl_get_propval_by_name(parent, PICL_PROP_CHILD, &(*child),
		sizeof (picl_nodehdl_t));
	switch (err) {
	case PICL_SUCCESS:
		break;
	case PICL_PROPNOTFOUND:
		err = PICL_INVALIDHANDLE;
		return (err);
	default:
#ifdef WORKFILE_DEBUG
		log_printf(dgettext(TEXT_DOMAIN,
			"Failed picl_get_propval_by_name with %s\n"),
			picl_strerror(err));
#endif
		return (err);
	}

	err = picl_get_propval_by_name(*child, PICL_PROP_NAME, name,
		PICL_PROPNAMELEN_MAX);

#ifdef WORKFILE_DEBUG
	if (err != PICL_SUCCESS) {
		log_printf(dgettext(TEXT_DOMAIN,
			"failed the get name for root\n"), 0);
		log_printf(dgettext(TEXT_DOMAIN, "%s\n"),
			picl_strerror(err), 0);
	}
#endif

	if (strcmp(name, child_name) == 0)
		return (err);

	while (err != PICL_PROPNOTFOUND) {
		err = picl_get_propval_by_name(*child, PICL_PROP_PEER,
			&(*child), sizeof (picl_nodehdl_t));
		switch (err) {
		case PICL_SUCCESS:
			err = picl_get_propval_by_name(*child, PICL_PROP_NAME,
				name, PICL_PROPNAMELEN_MAX);
			if (strcmp(name, child_name) == 0)
				return (err);
			break;
		case PICL_PROPNOTFOUND:
			break;
		default:
#ifdef WORKFILE_DEBUG
			log_printf(dgettext(TEXT_DOMAIN,
				"Failed picl_get_propval_by_name with %s\n"),
				picl_strerror(err), 0);
#endif
			return (err);
		}
	}
	err = PICL_INVALIDHANDLE;
	return (err);
}

int32_t
fill_device_from_id(picl_nodehdl_t device_id, char *assoc_id,
		picl_nodehdl_t *device)
{
	int32_t err;
	picl_prophdl_t tbl_hdl;
	picl_prophdl_t reference_property;

	err = picl_get_propval_by_name(device_id, assoc_id, &tbl_hdl,
		sizeof (picl_prophdl_t));
	if (err != PICL_SUCCESS) {
#ifdef WORKFILE_DEBUG
		if (err != PICL_INVALIDHANDLE) {
			log_printf(dgettext(TEXT_DOMAIN,
				"fill_device_from_id failure in "
				"picl_get_propval_by_name err is %s\n"),
					picl_strerror(err), 0);
		}
#endif
		return (err);
	}

	err = picl_get_next_by_row(tbl_hdl, &reference_property);
	if (err != PICL_SUCCESS) {
#ifdef WORKFILE_DEBUG
		log_printf(dgettext(TEXT_DOMAIN,
			"fill_device_from_id failure in picl_get_next_by_row"
			" err is %s\n"), picl_strerror(err), 0);
#endif
		return (err);
	}

	/* get node associated with reference property */
	err = picl_get_propval(reference_property, &(*device),
		sizeof (picl_nodehdl_t));

#ifdef WORKFILE_DEBUG
	if (err != 0) {
		log_printf(dgettext(TEXT_DOMAIN,
			"fill_device_from_id failure in picl_get_propval"
			" err is %s\n"), picl_strerror(err), 0);
	}
#endif
	return (err);
}

int32_t
fill_device_array_from_id(picl_nodehdl_t device_id, char *assoc_id,
	int32_t *number_of_devices, picl_nodehdl_t *device_array[])
{
	int32_t			err;
	int			i;
	picl_prophdl_t		tbl_hdl;
	picl_prophdl_t		entry;
	int			devs = 0;
	err = picl_get_propval_by_name(device_id, assoc_id, &tbl_hdl,
		sizeof (picl_prophdl_t));
	if ((err != PICL_SUCCESS) && (err != PICL_INVALIDHANDLE)) {
#ifdef WORKFILE_DEBUG
		log_printf(dgettext(TEXT_DOMAIN,
			"fill_device_array_from_id failure in "
			"picl_get_propval_by_name err is %s\n"),
				picl_strerror(err), 0);
#endif
		return (err);
	}

	entry = tbl_hdl;
	while (picl_get_next_by_row(entry, &entry) == 0)
		++devs;

	*device_array = calloc((devs), sizeof (picl_nodehdl_t));
	if (*device_array == NULL) {
#ifdef WORKFILE_DEBUG
		log_printf(dgettext(TEXT_DOMAIN,
			"fill_device_array_from_id failure getting memory"
			" for array\n"), 0);
#endif
		return (PICL_FAILURE);
	}

	entry = tbl_hdl;
	for (i = 0; i < (devs); i++) {
		err = picl_get_next_by_row(entry, &entry);
		if (err != 0) {
#ifdef WORKFILE_DEBUG
			log_printf(dgettext(TEXT_DOMAIN,
				"fill_device_array_from_id failure in "
				"picl_get_next_by_row err is %s\n"),
					picl_strerror(err), 0);
#endif
			return (err);
		}

		/* get node associated with reference property */
		err = picl_get_propval(entry, &((*device_array)[i]),
			sizeof (picl_nodehdl_t));
		if (err != 0) {
#ifdef WORKFILE_DEBUG
			log_printf(dgettext(TEXT_DOMAIN,
				"fill_device_array_from_id failure in "
				"picl_get_propval err is %s\n"),
					picl_strerror(err), 0);
#endif
			return (err);
		}
	}
	*number_of_devices = devs;
	return (err);
}

/*
 * This function provides formatting of the memory config
 * information that get_us3_mem_regs() and display_us3_banks() code has
 * gathered. It overrides the generic print_us3_memory_line() code
 * which prints an error message.
 */
void
print_us3_memory_line(int portid, int bank_id, uint64_t bank_size,
	char *bank_status, uint64_t dimm_size, uint32_t intlv, int seg_id)
{
	int		mcid;
	mcid = portid;

	log_printf(dgettext(TEXT_DOMAIN,
		"\n  %-1c   %2d    %2d      %4lldMB   %11-s  %4lldMB "
		"   %2d-way        %d"),
			'A' + DAK_GETSLOT(portid), mcid, (bank_id % 4),
			bank_size, bank_status, dimm_size, intlv, seg_id, 0);
}


/*
 * Fills in the i/o card list to be displayed later in display_pci();
 */
void
fill_pci_card_list(Prom_node * pci_instance, Prom_node * pci_card_node,
			struct io_card *pci_card,
			struct io_card **pci_card_list, char **slot_name_arr)
{
	Prom_node	*pci_bridge_node;
	Prom_node	*pci_parent_bridge;
	int		*int_val;
	int		pci_bridge = FALSE;
	int		pci_bridge_dev_no = -1;
	int		portid;
	int		pci_bus;
	char		buf[MAXSTRLEN];
	char		*slot_name = NULL;	/* info in "slot-names" prop */
	char		*child_name;
	char		*name;
	char		*type;
	void		*value;

	while (pci_card_node != NULL) {
		int is_pci = FALSE;
		type = NULL;
		name = NULL;
		/* If it doesn't have a name, skip it */
		name = (char *)get_prop_val(
				find_prop(pci_card_node, "name"));
		if (name == NULL) {
			pci_card_node = pci_card_node->sibling;
			continue;
		}

		/*
		 * Get the portid of the schizo that this card
		 * lives under.
		 */
		portid = -1;
		value = get_prop_val(find_prop(pci_instance, "portid"));
		if (value != NULL) {
			portid = *(int *)value;
		}
		pci_card->schizo_portid = portid;
		/*
		 * Find out whether this is PCI bus A or B
		 * using the 'reg' property.
		 */
		int_val = (int *)get_prop_val(find_prop(pci_instance, "reg"));

		if (int_val != NULL) {
			int_val++; /* skip over first integer */
			pci_bus = ((*int_val) & 0x7f0000);
			if (pci_bus == 0x600000)
				pci_card->pci_bus = 'A';
			else if (pci_bus == 0x700000)
				pci_card->pci_bus = 'B';
			else
				pci_card->pci_bus = '-';
		} else {
			pci_card->pci_bus = '-';
		}

		if ((pci_card->schizo_portid == 8) &&
		    (pci_card->pci_bus == 'A')) {
			pci_card_node = pci_card_node->sibling;
			continue;
		}

		/*
		 * get dev# and func# for this card from the
		 * 'reg' property.
		 */
		int_val = (int *)get_prop_val(
			find_prop(pci_card_node, "reg"));
		if (int_val != NULL) {
			pci_card->dev_no = (((*int_val) & 0xF800) >> 11);
			pci_card->func_no = (((*int_val) & 0x700) >> 8);
		} else {
			pci_card->dev_no = -1;
			pci_card->func_no = -1;
		}

		type = (char *)get_prop_val(
				find_prop(pci_card_node, "device_type"));
		/*
		 * If this is a pci-bridge, then store its dev#
		 * as its children nodes need this to get their slot#.
		 * We set the pci_bridge flag so that we know we are
		 * looking at a pci-bridge node. This flag gets reset
		 * every time we enter this while loop.
		 */

		/*
		 * Check for a PCI-PCI Bridge for PCI and cPCI
		 * IO Boards using the name and type properties.
		 */
		if ((type != NULL) && (strncmp(name, "pci", 3) == 0) &&
		    (strcmp(type, "pci") == 0)) {
			pci_bridge_node = pci_card_node;
			is_pci = TRUE;
			if (!pci_bridge) {
				pci_bridge_dev_no = pci_card->dev_no;
				pci_parent_bridge = pci_bridge_node;
				pci_bridge = TRUE;
			}
		}
		if ((pci_card->pci_bus == 'B') && (pci_card->dev_no == 1) &&
		    (!pci_bridge)) {
			pci_card_node = pci_card_node->sibling;
			continue;
		}

		/*
		 * Get slot-names property from slot_names_arr.
		 * If we are the child of a pci_bridge we use the
		 * dev# of the pci_bridge as an index to get
		 * the slot number. We know that we are a child of
		 * a pci-bridge if our parent is the same as the last
		 * pci_bridge node found above.
		 */
		if (pci_card->dev_no != -1) {
			/*
			 * We compare this cards parent node with the
			 * pci_bridge_node to see if it's a child.
			 */
			if (pci_card_node->parent != pci_instance &&
			    pci_bridge) {
				/* use dev_no of pci_bridge */
				if (pci_card->pci_bus == 'B') {
					slot_name =
					    slot_name_arr[pci_bridge_dev_no -2];
				} else {
					slot_name =
					    slot_name_arr[pci_bridge_dev_no -1];
				}
			} else {
				if (pci_card->pci_bus == 'B') {
				slot_name =
					slot_name_arr[pci_card->dev_no-2];
				} else {
				slot_name =
					slot_name_arr[pci_card->dev_no-1];
				}
			}

			if (slot_name != NULL &&
			    strlen(slot_name) != 0) {
				/* Slot num is last char in string */
				(void) snprintf(pci_card->slot_str, MAXSTRLEN,
				    "%c", slot_name[strlen(slot_name) - 1]);
			} else {
				(void) snprintf(pci_card->slot_str, MAXSTRLEN,
				    "-");
			}

		} else {
			(void) snprintf(pci_card->slot_str, MAXSTRLEN,
			    "%c", '-');
		}

		/*
		 * Check for failed status.
		 */
		if (node_failed(pci_card_node))
			strcpy(pci_card->status, "fail");
		else
			strcpy(pci_card->status, "ok");

		/* Get the model of this pci_card */
		value = get_prop_val(find_prop(pci_card_node, "model"));
		if (value == NULL)
			pci_card->model[0] = '\0';
		else {
			(void) snprintf(pci_card->model, MAXSTRLEN, "%s",
				(char *)value);
		}
		/*
		 * The card may have a "clock-frequency" but we
		 * are not interested in that. Instead we get the
		 * "clock-frequency" of the PCI Bus that the card
		 * resides on. PCI-A can operate at 33Mhz or 66Mhz
		 * depending on what card is plugged into the Bus.
		 * PCI-B always operates at 33Mhz.
		 */
		int_val = get_prop_val(find_prop(pci_instance,
			"clock-frequency"));
		if (int_val != NULL) {
			pci_card->freq = DAK_CLK_FREQ_TO_MHZ(*int_val);
		} else {
			pci_card->freq = -1;
		}

		/*
		 * Figure out how we want to display the name
		 */
		value = get_prop_val(find_prop(pci_card_node,
					"compatible"));
		if (value != NULL) {
			/* use 'name'-'compatible' */
			(void) snprintf(buf, MAXSTRLEN, "%s-%s", name,
				(char *)value);
		} else {
			/* just use 'name' */
			(void) snprintf(buf, MAXSTRLEN, "%s", name);
		}
		name = buf;

		/*
		 * If this node has children, add the device_type
		 * of the child to the name value of this pci_card->
		 */
		child_name = (char *)get_node_name(pci_card_node->child);
		if ((pci_card_node->child != NULL) &&
			(child_name != NULL)) {
			value = get_prop_val(find_prop(pci_card_node->child,
				"device_type"));
			if (value != NULL) {
				/* add device_type of child to name */
				(void) snprintf(pci_card->name, MAXSTRLEN,
				    "%s/%s (%s)", name, child_name,
					(char *)value);
			} else {
				/* just add childs name */
				(void) snprintf(pci_card->name, MAXSTRLEN,
				    "%s/%s", name, child_name);
			}
		} else {
			(void) snprintf(pci_card->name, MAXSTRLEN, "%s",
			    (char *)name);
		}

		/*
		 * If this is a pci-bridge, then add the word
		 * 'pci-bridge' to its model.  If we can't find
		 * a model, then we just describe what the device
		 * is based on some properties.
		 */
		if (pci_bridge) {
			if (strlen(pci_card->model) == 0) {
			    if (pci_card_node->parent == pci_bridge_node)
				(void) snprintf(pci_card->model, MAXSTRLEN,
				    "%s", "device on pci-bridge");
			    else if (pci_card_node->parent
					== pci_parent_bridge)
				(void) snprintf(pci_card->model, MAXSTRLEN,
					"%s", "pci-bridge/pci-bridge");
			    else
				(void) snprintf(pci_card->model, MAXSTRLEN,
				    "%s", "PCI-BRIDGE");
			}
			else
				(void) snprintf(pci_card->model, MAXSTRLEN,
				    "%s/pci-bridge", pci_card->model);
		}
		/* insert this pci_card in the list to be displayed later */

		*pci_card_list = insert_io_card(*pci_card_list, pci_card);

		/*
		 * If we are dealing with a pci-bridge, we need to move
		 * down to the children of this bridge if there are any.
		 *
		 * If we are not, we are either dealing with a regular
		 * card (in which case we move onto the sibling of this
		 * card) or we are dealing with a child of a pci-bridge
		 * (in which case we move onto the child's siblings or
		 * if there are no more siblings for this child, we
		 * move onto the parents siblings).
		 */
		pci_card_node = next_pci_card(pci_card_node, &pci_bridge,
			is_pci, pci_bridge_node,
			pci_parent_bridge, pci_instance);
	} /* end-while */
}


/*
 * Helper function for fill_pci_card_list().  Indicates which
 * card node to go to next.
 * Parameters:
 * -----------
 * Prom_node * curr_card: pointer to the current card node
 *
 * int * is_bridge: indicates whether or not the card (is | is on)
 *                  a pci bridge
 *
 * int is_pcidev: indicates whether or not the current card
 *                is a pci bridge
 *
 * Prom_node * curr_bridge: pointer to the current pci bridge.  Eg:
 *                          curr_card->parent.
 *
 * Prom_node * parent_bridge: pointer to the first pci bridge encountered.
 *			      we could have nested pci bridges, this would
 *			      be the first one.
 *
 * Prom_node * pci: pointer to the pci instance that we are attached to.
 *		    This would be parent_bridge->parent, or
 *		    curr_node->parent, if curr_node is not on a pci bridge.
 */
static Prom_node *
next_pci_card(Prom_node *curr_card, int *is_bridge, int is_pcidev,
		Prom_node *curr_bridge, Prom_node *parent_bridge,
		Prom_node *pci)
{
	Prom_node * curr_node = curr_card;
	if (*is_bridge) {
		/*
		 * is_pcidev is used to prevent us from following the
		 * children of something like a scsi device.
		 */
		if (curr_node->child != NULL && is_pcidev) {
			curr_node = curr_node->child;
		} else {
			curr_node = curr_node->sibling;
			if (curr_node == NULL) {
				curr_node = curr_bridge->sibling;

				while (curr_node == NULL &&
					curr_bridge != parent_bridge &&
					curr_bridge != NULL) {
					curr_node =
						curr_bridge->parent->sibling;
					curr_bridge = curr_bridge->parent;
					if (curr_node != NULL &&
					    curr_node->parent == pci) {
						break;
					}
				}

				if (curr_bridge == NULL ||
				    curr_node == NULL ||
				    curr_node->parent == pci ||
				    curr_bridge == parent_bridge ||
				    curr_node == parent_bridge) {
					*is_bridge = FALSE;
				}
			}
		}

	} else {
		curr_node = curr_node->sibling;
	}
	return (curr_node);
}