root/usr/src/lib/libprtdiag_psr/sparc/opl/common/opl_picl.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 *
 * Opl platform specific PICL functions.
 *
 *      called when :
 *      machine_type == MTYPE_OPL
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <kstat.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
#include <libintl.h>
#include <note.h>
#include <dlfcn.h>
#include <errno.h>
#include <sys/systeminfo.h>
#include <sys/openpromio.h>
#include <sys/sysmacros.h>
#include <picl.h>
#include "picldefs.h"
#include <pdevinfo.h>
#include <display.h>
#include <libprtdiag.h>
#include <alloca.h>
#include "opl_picl.h"
#include <sys/pci.h>
#include <sys/pci_tools.h>
#include <sys/types.h>

#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN     "SYS_TEST"
#endif

static picl_errno_t do_walk(picl_nodehdl_t rooth, const char *classname,
    void *c_args, picl_errno_t (*callback_fn)(picl_nodehdl_t hdl, void *args));
static int opl_get_node_by_name(picl_nodehdl_t rooth, char *name,
    picl_nodehdl_t *nodeh);
static picl_errno_t get_lane_width(char *device_path, int bus_no, int func_no,
    int dev_no, int *actual, int *maximum, uint32_t *speed_max,
    uint32_t *speed_at, int *type);
static int      opl_display_pci(int syserrlog, picl_nodehdl_t plafh);
static picl_errno_t opl_pci_callback(picl_nodehdl_t pcih, void *args);
static int opl_get_first_compatible_value(picl_nodehdl_t nodeh,
    char **outbuf);
static int picldiag_get_clock_freq(picl_nodehdl_t modh,
    uint32_t *freq, uint32_t *freq_max);
static uint64_t picldiag_get_uint_propval(picl_nodehdl_t modh,
    char *prop_name, int *ret);
static uint32_t read_long(int fd, int bus, int dev, int func,
    int offset, int *ret);
static uint8_t read_byte(int fd, int bus, int dev, int func, int offset,
    int *ret);
static uint16_t read_word(int fd, int bus, int dev, int func, int offset,
    int *ret);

/*
 * Collect I/O nodes information.
 */
/* ARGSUSED */
static picl_errno_t
opl_pci_callback(picl_nodehdl_t pcih, void *args)
{
        picl_errno_t    err = PICL_SUCCESS;
        picl_nodehdl_t  nodeh;
        picl_prophdl_t  proph;
        picl_propinfo_t pinfo;
        char            path[MAXSTRLEN];
        char            parent_path[MAXSTRLEN];
        static char     root_path[MAXSTRLEN];
        char            piclclass[PICL_CLASSNAMELEN_MAX];
        char            name[MAXSTRLEN];
        char            model[MAXSTRLEN];
        char            *compatible;
        char            binding_name[MAXSTRLEN];
        struct io_card  pci_card;
        char            status[6] = "N/A";
        int             portid = PROP_INVALID;
        int             *reg_val;
        int             board = PROP_INVALID;
        static int      saved_board = PROP_INVALID;
        static int      saved_portid = PROP_INVALID;
        int             actual = PROP_INVALID, maximum = PROP_INVALID;
        int             bus_type;
        int             rev_id = PROP_INVALID, dev_id = PROP_INVALID;
        int             ven_id = PROP_INVALID;
        size_t          prop_size;

        (void) memset(&pci_card, 0, sizeof (pci_card));

        err = picl_get_propval_by_name(pcih, PICL_PROP_CLASSNAME,
            piclclass, sizeof (piclclass));

        if (err !=  PICL_SUCCESS)
                /* Do not proceed to parse this branch */
                return (err);

        if (!IS_PCI(piclclass))
                /* Do not parse non-pci nodes */
                return (PICL_INVALIDARG);

        err = picl_get_propval_by_name(pcih, PICL_PROP_DEVFS_PATH, parent_path,
            sizeof (parent_path));
        if (err != PICL_SUCCESS)
                /* Do not proceed to parse this branch */
                return (err);
        err = picl_get_propval_by_name(pcih, OBP_PROP_BOARD_NUM, &board,
            sizeof (board));

        if (err == PICL_NORESPONSE)
                /* Do not proceed to parse this branch */
                return (err);
        else if (err != PICL_PROPNOTFOUND) {
                saved_board = board;
                /* Save board node's pathname */
                prop_size = sizeof (parent_path) + 1;
                if (prop_size > MAXSTRLEN)
                        prop_size = MAXSTRLEN;
                (void) strlcpy(root_path, parent_path, prop_size);
        }

        err = picl_get_propval_by_name
            (pcih, OBP_PROP_PORTID, &portid, sizeof (portid));

        if (err != PICL_PROPNOTFOUND)
                saved_portid = portid;

        /* Walk through the children */

        err = picl_get_propval_by_name(pcih, PICL_PROP_CHILD, &nodeh,
            sizeof (picl_nodehdl_t));

        while (err == PICL_SUCCESS) {
                uint32_t        freq_max = 0, freq_at = 0;

                err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME,
                    piclclass, sizeof (piclclass));
                if (err !=  PICL_SUCCESS)
                        /* Do not proceed to parse this node */
                        return (err);

                if (IS_EBUS(piclclass)) {
                        err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER,
                            &nodeh, sizeof (picl_nodehdl_t));
                        continue;
                }

                err = picl_get_propval_by_name(nodeh, PICL_PROP_DEVFS_PATH,
                    path, sizeof (path));
                if (err != PICL_SUCCESS) {
                        /* Do not proceed to parse this node */
                        return (err);
                }

                prop_size = sizeof (path) + 1;
                if (prop_size > MAXSTRLEN)
                        prop_size = MAXSTRLEN;
                (void) strlcpy(pci_card.notes, path, prop_size);

                pci_card.board = saved_board;
                pci_card.schizo_portid = saved_portid;

                /*
                 * Get bus#, dev# and func# for this card from 'reg' property.
                 */

                err = picl_get_propinfo_by_name
                    (nodeh, OBP_PROP_REG, &pinfo, &proph);
                if (err == PICL_SUCCESS) {
                        /* All of the array of bytes of "reg" have to be read */
                        reg_val = malloc(pinfo.size);
                        if (reg_val == NULL)
                                return (PICL_FAILURE);


                        err = picl_get_propval_by_name
                            (nodeh, OBP_PROP_REG, reg_val, pinfo.size);

                        if (err != PICL_SUCCESS) {
                                free(reg_val);
                                /* Do not proceed to parse this node */
                                return (err);
                        }

                        if (reg_val[0] != 0) {
                                pci_card.dev_no =
                                    (((reg_val[0]) & PCI_DEV_MASK) >> 11);
                                pci_card.func_no =
                                    (((reg_val[0]) & PCI_FUNC_MASK) >> 8);
                                pci_card.slot =
                                    (((reg_val[0]) & PCI_BUS_MASK) >> 16);
                        } else
                                free(reg_val);
                }

                err = get_lane_width(root_path, pci_card.slot, pci_card.dev_no,
                    pci_card.func_no, &actual, &maximum, &freq_max, &freq_at,
                    &bus_type);

                if (err != PICL_SUCCESS) {
                        /*
                         * get_lane_width will fail when run as non-root.
                         * Set bus_type to PCI_UNKN so that bus frequency,
                         * bus type and lane width will print as "--" or UNKN.
                         */
                        bus_type = PCI_UNKN;
                }


                err = picl_get_propval_by_name
                    (nodeh, PICL_PROP_NAME, name, sizeof (name));
                if (err != PICL_SUCCESS)
                        (void) strcpy(name, "");

                /*
                 * Get the name of this card. If binding_name is found,
                 * name will be <nodename>-<binding_name>
                 */

                err = picl_get_propval_by_name(nodeh, PICL_PROP_BINDING_NAME,
                    binding_name, sizeof (binding_name));
                if (err == PICL_PROPNOTFOUND) {
                        /*
                         * if compatible prop is found, name will be
                         * <nodename>-<compatible>
                         */
                        err = opl_get_first_compatible_value(nodeh,
                            &compatible);
                        if (err == PICL_SUCCESS) {
                                (void) strlcat(name, "-", MAXSTRLEN);
                                (void) strlcat(name, compatible, MAXSTRLEN);
                                free(compatible);
                        }
                } else if (err != PICL_SUCCESS) {
                        /* No binding-name or compatible */
                        (void) strcpy(binding_name, "N/A");
                } else if (strcmp(name, binding_name) != 0) {
                        (void) strlcat(name, "-", MAXSTRLEN);
                        (void) strlcat(name, binding_name, MAXSTRLEN);
                }


                prop_size = sizeof (name) + 1;
                if (prop_size > MAXSTRLEN)
                        prop_size =  MAXSTRLEN;
                (void) strlcpy(pci_card.name, name, prop_size);

                /* Get the status of the card */
                err = picl_get_propval_by_name
                    (nodeh, PICL_PROP_STATUS, status, sizeof (status));


                /* Get the model of this card */

                err = picl_get_propval_by_name
                    (nodeh, OBP_PROP_MODEL, model, sizeof (model));
                prop_size = sizeof (model) + 1;
                if (prop_size > MAXSTRLEN)
                        prop_size =  MAXSTRLEN;
                if (err != PICL_SUCCESS)
                        (void) strcpy(model, "N/A");
                (void) strlcpy(pci_card.model, model, prop_size);

                if (bus_type == PCI)
                        (void) strlcpy(pci_card.bus_type,
                            "PCI", sizeof (pci_card.bus_type));
                else if (bus_type == PCIX)
                        (void) strlcpy(pci_card.bus_type,
                            "PCIx", sizeof (pci_card.bus_type));
                else if (bus_type == PCIE)
                        (void) strlcpy(pci_card.bus_type,
                            "PCIe", sizeof (pci_card.bus_type));
                else
                        (void) strlcpy(pci_card.bus_type,
                            "UNKN", sizeof (pci_card.bus_type));

                /* Get revision id */
                err = picl_get_propval_by_name
                    (nodeh, OBP_PROP_REVISION_ID, &rev_id, sizeof (rev_id));

                /* Get device id */
                err = picl_get_propval_by_name
                    (nodeh, OBP_PROP_DEVICE_ID, &dev_id, sizeof (dev_id));

                /* Get vendor id */
                err = picl_get_propval_by_name
                    (nodeh, OBP_PROP_VENDOR_ID, &ven_id, sizeof (ven_id));

                /*
                 * prtdiag -v prints all devices
                 */

                /* Print board number */
                log_printf("%02d  ", pci_card.board);
                /* Print IO Type */
                log_printf("%-5.5s ", pci_card.bus_type);

                log_printf("%-3d  ", pci_card.schizo_portid);
                log_printf("%4x, %4x, %4x     ", rev_id, dev_id, ven_id);

                log_printf("%3d, %2d, %2d",
                    pci_card.slot, pci_card.dev_no, pci_card.func_no);

                /* Print status */
                log_printf("  %-5.5s ", status);

                /* Print Lane widths, Max/Sup Freq, Speed */
                if (bus_type == PCIE) {
                        PRINT_FMT(actual, maximum);
                } else if (bus_type == PCIX) {
                        PRINT_FREQ_FMT(freq_at, freq_max);
                } else if (bus_type == PCI) {
                        err = picldiag_get_clock_freq(nodeh, &freq_at,
                            &freq_max);
                        PRINT_FREQ_FMT(freq_at, freq_max);
                } else
                        log_printf(" -- , --   ");

                /* Print Card Name */
                log_printf("%-30.30s", pci_card.name);

                /* Print Card Model */
                log_printf(" %-20.20s", pci_card.model);

                log_printf("\n");

                log_printf("%4s%-100.100s", " ", pci_card.notes);
                log_printf("\n");
                log_printf("\n");


                err = picl_get_propval_by_name
                    (nodeh, PICL_PROP_PEER, &nodeh, sizeof (picl_nodehdl_t));

        }

        return (PICL_WALK_CONTINUE);
}

/*
 * opl_display_pci
 * Display all the PCI IO cards on this board.
 */
static int
opl_display_pci(int syserrlog, picl_nodehdl_t plafh)
{
        picl_errno_t err;
        char    *fmt = "%-3s %-5s %-4s %-20s %-11s %-5s %-11s %-30s %-20s";
        char    *fmt2 = "%-16s";
        static int banner = FALSE; /* Have we printed the column headings? */

        if (banner == FALSE) {
                log_printf("\n", 0);
                log_printf("=========================", 0);
                log_printf(dgettext(TEXT_DOMAIN, " IO Devices "), 0);
                log_printf("=========================", 0);
                log_printf("\n", 0);
                log_printf("\n", 0);
                log_printf(fmt, "", "IO", "", "", "", "", "Lane/Frq",
                    "", "", 0);
                log_printf("\n", 0);

                log_printf(fmt, "LSB", "Type", "LPID", "  RvID,DvID,VnID",
                    "  BDF", "State", "Act,  Max", "Name", "Model", 0);

                log_printf("\n");

                log_printf(fmt,
                    "---", "-----", "----", "  ------------------",
                    "  ---------", "-----", "-----------",
                    "------------------------------",
                    "--------------------", 0);
                log_printf("\n");
                log_printf(fmt2, "    Logical Path");
                log_printf("\n");
                log_printf(fmt2, "    ------------");
                log_printf("\n");
                banner = TRUE;
        }

        err = do_walk(plafh, PICL_CLASS_PCI, PICL_CLASS_PCI, opl_pci_callback);
        return (err);
}


/*
 * return the first compatible value
 */
static int
opl_get_first_compatible_value(picl_nodehdl_t nodeh, char **outbuf)
{
        picl_errno_t    err;
        picl_prophdl_t  proph;
        picl_propinfo_t pinfo;
        picl_prophdl_t  tblh;
        picl_prophdl_t  rowproph;
        char            *pval;

        err = picl_get_propinfo_by_name(nodeh, OBP_PROP_COMPATIBLE,
            &pinfo, &proph);
        if (err != PICL_SUCCESS)
                return (err);

        if (pinfo.type == PICL_PTYPE_CHARSTRING) {
                pval = malloc(pinfo.size);
                if (pval == NULL)
                        return (PICL_FAILURE);
                err = picl_get_propval(proph, pval, pinfo.size);
                if (err != PICL_SUCCESS) {
                        free(pval);
                        return (err);
                }
                *outbuf = pval;
                return (PICL_SUCCESS);
        }

        if (pinfo.type != PICL_PTYPE_TABLE)
                return (PICL_FAILURE);

        /* get first string from table */
        err = picl_get_propval(proph, &tblh, pinfo.size);
        if (err != PICL_SUCCESS)
                return (err);

        err = picl_get_next_by_row(tblh, &rowproph);
        if (err != PICL_SUCCESS)
                return (err);

        err = picl_get_propinfo(rowproph, &pinfo);
        if (err != PICL_SUCCESS)
                return (err);

        pval = malloc(pinfo.size);
        if (pval == NULL)
                return (PICL_FAILURE);

        err = picl_get_propval(rowproph, pval, pinfo.size);
        if (err != PICL_SUCCESS) {
                free(pval);
                return (err);
        }

        *outbuf = pval;
        return (PICL_SUCCESS);
}

int
do_piclinfo(int syserrlog)
{
        picl_nodehdl_t rooth;           /* root PICL node for IO display */
        picl_nodehdl_t plafh;           /* Platform PICL node for IO display */

        picl_errno_t err;

        err = picl_initialize();
        if (err != PICL_SUCCESS) {
                (void) log_printf("picl_initialize failed: %s\n",
                    picl_strerror(err));
                return (err);
        }


        err = picl_get_root(&rooth);
        if (err != PICL_SUCCESS) {
                (void) log_printf("Getting root node failed: %s\n",
                    picl_strerror(err));
                return (err);
        }

        err = opl_get_node_by_name(rooth, PICL_NODE_PLATFORM, &plafh);

        if (err != PICL_SUCCESS) {
                (void) log_printf("Getting nodes by name failed: %s\n",
                    picl_strerror(err));
                return (err);
        }

        err = opl_display_pci(syserrlog, plafh);

        (void) picl_shutdown();

        return (err);
}

/*
 * search children to get the node by the nodename
 */
static int
opl_get_node_by_name(picl_nodehdl_t rooth, char *name,
    picl_nodehdl_t *nodeh)
{
        picl_nodehdl_t  childh;
        int             err;
        char            *nodename;

        nodename = alloca(strlen(name) + 1);
        if (nodename == NULL)
                return (PICL_FAILURE);

        err = picl_get_propval_by_name(rooth, PICL_PROP_CHILD, &childh,
            sizeof (picl_nodehdl_t));

        while (err == PICL_SUCCESS) {
                err = picl_get_propval_by_name(childh, PICL_PROP_NAME,
                    nodename, (strlen(name) + 1));
                if (err != PICL_SUCCESS) {
                        err = picl_get_propval_by_name(childh, PICL_PROP_PEER,
                            &childh, sizeof (picl_nodehdl_t));
                        continue;
                }

                if (strcmp(nodename, name) == 0) {
                        *nodeh = childh;
                        return (PICL_SUCCESS);
                }

                err = picl_get_propval_by_name(childh, PICL_PROP_PEER,
                    &childh, sizeof (picl_nodehdl_t));
        }

        return (err);
}

static int
open_root_complex(char *root_complex)
{
        char *path;
        static char device_str[] = {"/devices"};
        static char devctl_str[] = {":reg"};
        int fd;

        path = malloc(
            strlen(root_complex) + sizeof (device_str) + sizeof (devctl_str));
        if (path == NULL)
                return (PICL_FAILURE);
        (void) strcpy(path, device_str);
        (void) strcat(path, root_complex);
        (void) strcat(path, devctl_str);

        if ((fd = open(path, O_RDWR)) == -1) {
                return (-1);
        }
        return (fd);
}

static uint32_t
read_long(int fd, int bus, int dev, int func, int offset, int *ret)
{
        int rval;
        pcitool_reg_t prg;

        prg.user_version = PCITOOL_VERSION;
        prg.barnum = 0;
        prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 +
            PCITOOL_ACC_ATTR_ENDN_LTL;
        prg.bus_no = bus;
        prg.dev_no = dev;
        prg.func_no = func;
        prg.offset = offset;
        rval = ioctl(fd, PCITOOL_DEVICE_GET_REG, &prg);
        if (rval != 0) {
                log_printf("DEV_GET failed %d %s\n", rval, strerror(errno));
                log_printf("%d.%d.%d offset 0x%x\n", bus, dev, func, offset);
        }
        *ret = rval;
        return ((uint32_t)prg.data);
}

static uint16_t
read_word(int fd, int bus, int dev, int func, int offset, int *ret)
{
        int rval;
        pcitool_reg_t prg;

        prg.user_version = PCITOOL_VERSION;
        prg.barnum = 0;
        prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_2 +
            PCITOOL_ACC_ATTR_ENDN_LTL;
        prg.bus_no = bus;
        prg.dev_no = dev;
        prg.func_no = func;
        prg.offset = offset;
        rval = ioctl(fd, PCITOOL_DEVICE_GET_REG, &prg);
        if (rval != 0) {
                log_printf("DEV_GET failed %d %s\n", rval, strerror(errno));
                log_printf("%d.%d.%d offset 0x%x\n", bus, dev, func, offset);
        }
        *ret = rval;
        return ((uint16_t)prg.data);
}

static uint8_t
read_byte(int fd, int bus, int dev, int func, int offset, int *ret)
{
        int rval;
        pcitool_reg_t prg;

        prg.user_version = PCITOOL_VERSION;
        prg.barnum = 0;
        prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_1 +
            PCITOOL_ACC_ATTR_ENDN_LTL;
        prg.bus_no = bus;
        prg.dev_no = dev;
        prg.func_no = func;
        prg.offset = offset;
        rval = ioctl(fd, PCITOOL_DEVICE_GET_REG, &prg);
        if (rval != 0) {
                log_printf("DEV_GET failed %d %s\n", rval, strerror(errno));
                log_printf("%d.%d.%d offset 0x%x\n", bus, dev, func, offset);
        }
        *ret = rval;
        return ((uint8_t)prg.data);
}


static picl_errno_t
get_lane_width
        (char *device_path, int bus, int dev, int func, int *actual,
        int *maximum, uint32_t *speed_max, uint32_t *speed_at, int *type)
{
        uint_t cap_ptr, cap_reg, link_status, link_cap, capid;
        int fd, ret;

        if (device_path == NULL)
                return (PICL_FAILURE);

        fd = open_root_complex(device_path);
        if (fd == -1)
                return (PICL_FAILURE);

        /*
         * Link Capabilities and Link Status registers are in the
         * PCI-E capabilities register.  They are at offset
         * 0xc and 0x12 respectively. They are documented in section
         * 7.8 of the PCI Express Base Specification. The address of
         * that structure is not fixed, it's kind of a linked list.
         * The Capabilities Pointer reg (8 bits) is always at 0x34.
         * It contains a pointer to the first capabilities structure.
         * For each capability structure, the first 8 bits is the capability
         * ID. The next 8 bits is the pointer to the next structure.
         * If the Next Cap register is zero, it's the end of the list.
         * The capability ID for the PCI-E strucutre is 0x10.  The idea
         * is to follow the links until you find a Cap ID of 0x10, then
         * read the registers at 0xc and 0x12 from there.
         * If there's no Cap ID 0x10, then it's not a PCI-E device.
         */

        cap_ptr = read_byte(fd, bus, dev, func, PCI_CONF_CAP_PTR, &ret);
        if (ret != 0) {
                /* ioctl failure */
                close(fd);
                return (PICL_FAILURE);
        }
        cap_reg = read_word(fd, bus, dev, func, cap_ptr, &ret);
        if (ret != 0) {
                /* ioctl failure */
                close(fd);
                return (PICL_FAILURE);
        }
        *type = PCI;
        capid = cap_reg & PCI_CAP_MASK;
        while (cap_ptr != 0) {

                if (capid == PCI_CAP_ID_PCI_E) {
                        link_cap = read_long(fd, bus, dev, func, cap_ptr +
                            PCIE_LINKCAP, &ret);
                        if (ret != 0) {
                                close(fd);
                                return (PICL_FAILURE);
                        }
                        link_status = read_word(fd, bus, dev, func,
                            cap_ptr + PCIE_LINKSTS, &ret);
                        if (ret != 0) {
                                close(fd);
                                return (PICL_FAILURE);
                        }
                        *actual = ((link_status >> PCI_LINK_SHIFT) &
                            PCI_LINK_MASK);
                        *maximum = ((link_cap >> PCI_LINK_SHIFT) &
                            PCI_LINK_MASK);
                        *type = PCIE;
                } else if (capid == PCI_CAP_ID_PCIX) {
                        uint32_t pcix_status;
                        uint8_t hdr_type;
                        int max_speed = PCI_FREQ_66;

                        hdr_type = read_byte
                            (fd, bus, dev, func, PCI_CONF_HEADER, &ret);
                        if (ret != 0) {
                                /* ioctl failure */
                                close(fd);
                                return (PICL_FAILURE);
                        }
                        *type = PCIX;
                        if ((hdr_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) {
                                /* This is a PCI-X bridge */
                                uint16_t sec_status, mode;
                                sec_status = read_word(fd, bus, dev, func,
                                    cap_ptr + PCI_PCIX_SEC_STATUS, &ret);
                                if (ret != 0) {
                                        /* ioctl failure */
                                        close(fd);
                                        return (PICL_FAILURE);
                                }
                                if (sec_status & PCI_SEC_133)
                                        max_speed = PCI_FREQ_133;
                                if (sec_status & PCI_SEC_266)
                                        max_speed = PCI_FREQ_266;
                                if (sec_status & PCI_SEC_533)
                                        max_speed = PCI_FREQ_533;
                                *speed_max = max_speed;
                                mode = (sec_status >> PCI_CLASS_BRIDGE) &
                                    PCI_BRIDGE_MC;
                                if (mode) {
                                        int speed;
                                        if (mode == PCI_MODE_66)
                                                speed = PCI_FREQ_66;
                                        else if (mode == PCI_MODE_100)
                                                speed = PCI_FREQ_100;
                                        else if (mode == PCI_MODE_133)
                                                speed = PCI_FREQ_133;
                                        *speed_at = speed;
                                }

                        } else {  /* Leaf device */
                                pcix_status = read_long(fd, bus, dev, func,
                                    cap_ptr + PCI_PCIX_STATUS, &ret);
                                if (ret != 0) {
                                        /* ioctl failure */
                                        close(fd);
                                        return (PICL_FAILURE);
                                }
                                if (pcix_status &
                                    (PCI_LEAF_ULONG << PCI_SHIFT_133))
                                        max_speed = PCI_FREQ_133;
                                if (pcix_status &
                                    (PCI_LEAF_ULONG << PCI_SHIFT_266))
                                        max_speed = PCI_FREQ_266;
                                if (pcix_status &
                                    (PCI_LEAF_ULONG << PCI_SHIFT_533))
                                        max_speed = PCI_FREQ_533;
                                *speed_max = max_speed;
                        }
                }
                cap_ptr = (cap_reg >> PCI_REG_FUNC_SHIFT);
                cap_reg = read_word(fd, bus, dev, func, cap_ptr, &ret);
                if (ret != 0) {
                        /* ioctl failure */
                        close(fd);
                        return (PICL_FAILURE);
                }
                capid = cap_reg & PCI_CAP_MASK;
        }

        if (close(fd) == -1) {
                return (PICL_FAILURE);
        }

        return (PICL_SUCCESS);
}

static int
is_66mhz_capable(picl_nodehdl_t nodeh)
{
        picl_errno_t    err;
        picl_prophdl_t  proph;
        picl_propinfo_t pinfo;

        err = picl_get_propinfo_by_name(nodeh, OBP_PROP_66MHZ_CAPABLE,
            &pinfo, &proph);
        if (err == PICL_SUCCESS)
                return (1);
        return (0);
}

/*
 * get the clock frequency
 */
static int
picldiag_get_clock_freq(picl_nodehdl_t modh, uint32_t *freq, uint32_t *freq_max)
{
        int             err;
        uint64_t        clk_freq;

        *freq_max = PCI_FREQ_33;
        if (is_66mhz_capable(modh))
                *freq_max = PCI_FREQ_66;
        clk_freq = picldiag_get_uint_propval(modh, OBP_PROP_CLOCK_FREQ, &err);
        if (err != PICL_SUCCESS)
                return (err);

        *freq = ROUND_TO_MHZ(clk_freq);

        return (PICL_SUCCESS);
}

static uint64_t
picldiag_get_uint_propval(picl_nodehdl_t modh, char *prop_name, int *ret)
{
        int             err;
        picl_prophdl_t  proph;
        picl_propinfo_t pinfo;
        uint8_t         uint8v;
        uint16_t        uint16v;
        uint32_t        uint32v;
        uint64_t        uint64v;

        err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph);
        if (err != PICL_SUCCESS) {
                *ret = err;
                return (0);
        }

        /*
         * If it is not an int or uint prop, return failure
         */
        if ((pinfo.type != PICL_PTYPE_INT) &&
            (pinfo.type != PICL_PTYPE_UNSIGNED_INT)) {
                *ret = PICL_FAILURE;
                return (0);
        }


        /* uint prop */

        switch (pinfo.size) {
        case sizeof (uint8_t):
                err = picl_get_propval(proph, &uint8v, sizeof (uint8v));
                *ret = err;
                return (uint8v);
        case sizeof (uint16_t):
                err = picl_get_propval(proph, &uint16v, sizeof (uint16v));
                *ret = err;
                return (uint16v);
        case sizeof (uint32_t):
                err = picl_get_propval(proph, &uint32v, sizeof (uint32v));
                *ret = err;
                return (uint32v);
        case sizeof (uint64_t):
                err = picl_get_propval(proph, &uint64v, sizeof (uint64v));
                *ret = err;
                return (uint64v);
        default:        /* not supported size */
                *ret = PICL_FAILURE;
                return (0);
        }
}

/*
 * recursively visit all nodes
 */
static picl_errno_t
do_walk(picl_nodehdl_t rooth, const char *classname,
    void *c_args, picl_errno_t (*callback_fn)(picl_nodehdl_t hdl, void *args))
{
        picl_errno_t    err;
        picl_nodehdl_t  chdh;
        char            classval[PICL_CLASSNAMELEN_MAX];

        err = picl_get_propval_by_name(rooth, PICL_PROP_CHILD, &chdh,
            sizeof (chdh));
        while (err == PICL_SUCCESS) {
                err = picl_get_propval_by_name(chdh, PICL_PROP_NAME,
                    classval, sizeof (classval));
                if (err != PICL_SUCCESS)
                        return (err);

                err = callback_fn(chdh, c_args);

                if ((err = do_walk(chdh, classname, c_args, callback_fn)) !=
                    PICL_WALK_CONTINUE)
                        return (err);

                err = picl_get_propval_by_name(chdh, PICL_PROP_PEER, &chdh,
                    sizeof (chdh));
        }
        if (err == PICL_PROPNOTFOUND)   /* end of a branch */
                return (PICL_WALK_CONTINUE);
        return (err);
}

int
get_proc_mode(void)
{
        picl_nodehdl_t nodeh;
        picl_prophdl_t  proph;
        picl_errno_t err;

        err = picl_initialize();
        if (err != PICL_SUCCESS) {
                (void) log_printf("picl_initialize failed: %s\n",
                    picl_strerror(err));
                return (err);
        }

        err = picl_get_node_by_path("/platform",  &nodeh);
        if (err != PICL_SUCCESS) {
                (void) log_printf("Getting plat node failed: %s\n",
                    picl_strerror(err));
                return (err);
        }

        err = picl_get_prop_by_name(nodeh, "SPARC64-VII-mode",  &proph);
        if (err != PICL_SUCCESS) {
                /* Do not display error message */
                return (err);
        }

        (void) picl_shutdown();

        return (err);
}