root/usr/src/lib/libprtdiag_psr/sparc/littleneck/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.
 *
 * Littleneck Platform specific functions.
 */

#include <libprtdiag.h>
#include <sys/mc.h>


static Prom_node *dev_next_node_by_compat(Prom_node *root, char *model);
static Prom_node *dev_find_node_by_compat(Prom_node *root, char *model);

static Board_node *littleneck_insert_board(Sys_tree *root, int board);
static Board_node *littleneck_find_board(Sys_tree *root, int board);

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);

void    add_node(Sys_tree *root, Prom_node *pnode);
int     do_prominfo(int syserrlog,
                    char *pgname,
                    int log_flag,
                    int prt_flag);

void    *get_prop_val(Prop *prop);
Prop    *find_prop(Prom_node *pnode, char *name);
char    *get_node_name(Prom_node *pnode);
char    *get_node_type(Prom_node *pnode);

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);


#define LNECK_MAX_SLOTS_PER_IO_BD 9
#define LNECK_CLK_FREQ_TO_MHZ(x)        (((x) + 500000) / 1000000)

/* This is stuff to get the HW Revisions stuff to work */

#define LNECK_SAFARI_ID_MASK            0x1F    /* 5 bits */
#define LNECK_NODE_MASK                 0x1F    /* 5 bits */
#define LNECK_PORTID_NODE_SHIFT         5
#define LNECK_MIN_CPU_SAFARI_ID         0       /* 0x00 */
#define LNECK_MAX_CPU_SAFARI_ID         23      /* 0x17 */
#define LNECK_MIN_IO_SAFARI_ID          24      /* 0x18 */
#define LNECK_MAX_IO_SAFARI_ID          31      /* 0x1F */
#define NUM_MBANKS_PER_MC               4

/*
 * LNECK_PORTID_TO_SAFARI_ID
 *
 * Calculates the Safari Agent ID from the portid.
 */
#define LNECK_PORTID_TO_SAFARI_ID(portid)  ((portid) & LNECK_SAFARI_ID_MASK)

/*
 * LNECK_PORTID_IS_CPU_TYPE
 *
 * If the portid associated with a CPU board is passed in, TRUE is returned,
 * otherwise FALSE.
 */
#define LNECK_PORTID_IS_CPU_TYPE(portid) \
        (((((portid) & LNECK_SAFARI_ID_MASK) >= LNECK_MIN_CPU_SAFARI_ID) && \
        (((portid) & LNECK_SAFARI_ID_MASK) <= LNECK_MAX_CPU_SAFARI_ID)) ? \
                                                                TRUE: FALSE)

/*
 * LNECK_PORTID_TO_NODEID
 *
 * Calculates the SSM NodeID from the portid
 */
#define LNECK_PORTID_TO_NODEID(portid) (((portid) >> LNECK_PORTID_NODE_SHIFT) \
                                                        & LNECK_NODE_MASK)

/*
 * LNECK_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 LNECK_CPU_BD_PORTID_TO_BD_NUM(portid) \
        ((LNECK_PORTID_IS_CPU_TYPE(portid)) ? \
                (((portid) & LNECK_SAFARI_ID_MASK) / 4) : (-1))

/*
 * LNECK_PORTID_IS_IO_TYPE
 *
 * If the portid associated with an IO board is passed in, TRUE is returned,
 * otherwise FALSE.
 */
#define LNECK_PORTID_IS_IO_TYPE(portid) \
        (((((portid) & LNECK_SAFARI_ID_MASK) >= LNECK_MIN_IO_SAFARI_ID) && \
        (((portid) & LNECK_SAFARI_ID_MASK) <= LNECK_MAX_IO_SAFARI_ID)) ? \
                                                                TRUE: FALSE)

/*
 * LNECK_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 LNECK_IO_BD_PORTID_TO_BD_NUM(portid) \
        (LNECK_PORTID_IS_IO_TYPE(portid) ? \
                (((((portid) & LNECK_SAFARI_ID_MASK) - 24) / 2) + 6) : (-1))

/*
 * LNECK_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 LNECK_PORTID_TO_BOARD_NUM(portid) \
        ((LNECK_PORTID_IS_CPU_TYPE(portid)) ? \
                (LNECK_CPU_BD_PORTID_TO_BD_NUM(portid)) : \
        ((LNECK_PORTID_IS_IO_TYPE(portid)) ? \
                LNECK_IO_BD_PORTID_TO_BD_NUM(portid) : (-1)))


/*
 * 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 */
}

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"));
                log_printf(dgettext(TEXT_DOMAIN, "%s\n"), picl_strerror(err));
        }
#endif

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

        while (err != PICL_PROPNOTFOUND) {
#ifdef WORKFILE_DEBUG
                log_printf(dgettext(TEXT_DOMAIN, "child name is %s\n"), name);
#endif
                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));
#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));
                }
#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));
#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));
        }
#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));
#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 WORFILE_DEBUG
                log_printf(dgettext(TEXT_DOMAIN,
                "fill_device_array_from_id failure getting memory"
                " for array\n"));
#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));
#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));
#endif

                        return (err);
                }
        }
        *number_of_devices = devs;
        return (err);
}

/*
 * Add a board to the system list in order (sorted by board#).
 * Initialize all pointer fields to NULL.
 */
static Board_node *
littleneck_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 *
littleneck_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);
}

/*
 * 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   = LNECK_PORTID_TO_BOARD_NUM(portid);

        /* find the board node with the same board number */
        if ((bnode = littleneck_find_board(root, board)) == NULL) {
                bnode = littleneck_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;
        }
}

/*
 * 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;
        char            board_id[2];
        board_id[0] = 'A'; /* it will usually be, A, there's only one mc!  */
        board_id[1] = 'B'; /* redundant. */
        mcid            = LNECK_PORTID_TO_SAFARI_ID(portid);

        log_printf(dgettext(TEXT_DOMAIN,
        "\n C%-1c   %2d    %2d      %4lldMB   %11-s  %4lldMB "
        "   %2d-way        %d"),
            board_id[portid], mcid, (bank_id % 4), bank_size,
            bank_status, dimm_size, intlv, seg_id, 0);
}

/*
 * We call do_devinfo() in order to use the libdevinfo device tree
 * instead of OBP's device tree.
 */
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. (When using libdevinfo)
 */
void *
get_prop_val(Prop *prop)
{
        if (prop == NULL)
                return (NULL);

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

/*
 * Search a Prom node and retrieve the property with the correct
 * name. (When using libdevinfo)
 */
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);
}

/*
 * This function searches through the properties of the node passed in
 * and returns a pointer to the value of the name property.
 * (When using libdevinfo)
 */
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 device_type property.
 * (When using libdevinfo)
 */
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);
}


/*
 * 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 = '-';
                }
                /*
                 * 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;
                }

                /* We only have one slot on bus A */
                if ((pci_card->pci_bus == 'A') && (pci_card->dev_no != 1) &&
                        !pci_bridge) {
                        pci_card_node = pci_card_node->sibling;
                        continue;
                }
                if ((pci_card->pci_bus == 'B') && (pci_card->dev_no > 4)) {
                        pci_card_node = pci_card_node->sibling;
                        continue;
                }

                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;
                        }
                }

                /*
                 * 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 */
                                slot_name =
                                    slot_name_arr[pci_bridge_dev_no -1];
                        } 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);
                        /* Skip sgsbbc nodes, they are not cards */
                        if (strcmp(pci_card->model, "SUNW,sgsbbc") == 0) {
                                pci_card_node = pci_card_node->sibling;
                                continue;
                        }
                }
                /*
                 * 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 = LNECK_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);
}