OpenSolaris_b135/lib/libprtdiag_psr/sparc/opl/common/opl_picl.c

Compare this file to the similar file:
Show the results in this format:

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