summaryrefslogtreecommitdiff
path: root/usr/src/psm/promif/ieee1275/common/prom_node.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/psm/promif/ieee1275/common/prom_node.c')
-rw-r--r--usr/src/psm/promif/ieee1275/common/prom_node.c294
1 files changed, 294 insertions, 0 deletions
diff --git a/usr/src/psm/promif/ieee1275/common/prom_node.c b/usr/src/psm/promif/ieee1275/common/prom_node.c
new file mode 100644
index 0000000000..95ff877149
--- /dev/null
+++ b/usr/src/psm/promif/ieee1275/common/prom_node.c
@@ -0,0 +1,294 @@
+/*
+ * 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 1994,1998-2000,2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/promif.h>
+#include <sys/promimpl.h>
+
+/*
+ * Routines for walking the PROMs devinfo tree
+ */
+dnode_t
+prom_nextnode(dnode_t nodeid)
+{
+ cell_t ci[5];
+
+ ci[0] = p1275_ptr2cell("peer"); /* Service name */
+ ci[1] = (cell_t)1; /* #argument cells */
+ ci[2] = (cell_t)1; /* #result cells */
+ ci[3] = p1275_dnode2cell(nodeid); /* Arg1: input phandle */
+ ci[4] = p1275_dnode2cell(OBP_NONODE); /* Res1: Prime result */
+
+ promif_preprom();
+ (void) p1275_cif_handler(&ci);
+ promif_postprom();
+
+ return (p1275_cell2dnode(ci[4])); /* Res1: peer phandle */
+}
+
+dnode_t
+prom_childnode(dnode_t nodeid)
+{
+ cell_t ci[5];
+
+ ci[0] = p1275_ptr2cell("child"); /* Service name */
+ ci[1] = (cell_t)1; /* #argument cells */
+ ci[2] = (cell_t)1; /* #result cells */
+ ci[3] = p1275_dnode2cell(nodeid); /* Arg1: input phandle */
+ ci[4] = p1275_dnode2cell(OBP_NONODE); /* Res1: Prime result */
+
+ promif_preprom();
+ (void) p1275_cif_handler(&ci);
+ promif_postprom();
+
+ return (p1275_cell2dnode(ci[4])); /* Res1: child phandle */
+}
+
+/*
+ * prom_walk_devs() implements a generic walker for the OBP tree; this
+ * implementation uses an explicitly managed stack in order to save the
+ * overhead of a recursive implementation.
+ */
+void
+prom_walk_devs(dnode_t node, int (*cb)(dnode_t, void *, void *), void *arg,
+ void *result)
+{
+ dnode_t stack[OBP_STACKDEPTH];
+ int stackidx = 0;
+
+ if (node == OBP_NONODE || node == OBP_BADNODE) {
+ prom_panic("Invalid node specified as root of prom tree walk");
+ }
+
+ stack[0] = node;
+
+ for (;;) {
+ dnode_t curnode = stack[stackidx];
+ dnode_t child;
+
+ /*
+ * We're out of stuff to do at this level, bump back up a level
+ * in the tree, and move to the next node; if the new level
+ * will be level -1, we're done.
+ */
+ if (curnode == OBP_NONODE || curnode == OBP_BADNODE) {
+ stackidx--;
+
+ if (stackidx < 0)
+ return;
+
+ stack[stackidx] = prom_nextnode(stack[stackidx]);
+ continue;
+ }
+
+ switch ((*cb)(curnode, arg, result)) {
+
+ case PROM_WALK_TERMINATE:
+ return;
+
+ case PROM_WALK_CONTINUE:
+ /*
+ * If curnode has a child, traverse to it,
+ * otherwise move to curnode's sibling.
+ */
+ child = prom_childnode(curnode);
+ if (child != OBP_NONODE && child != OBP_BADNODE) {
+ stackidx++;
+ stack[stackidx] = child;
+ } else {
+ stack[stackidx] =
+ prom_nextnode(stack[stackidx]);
+ }
+ break;
+
+ default:
+ prom_panic("unrecognized walk directive");
+ }
+ }
+}
+
+/*
+ * prom_findnode_bydevtype() searches the prom device subtree rooted at 'node'
+ * and returns the first node whose device type property matches the type
+ * supplied in 'devtype'.
+ */
+static int
+bytype_cb(dnode_t node, void *arg, void *result)
+{
+ if (prom_devicetype(node, (char *)arg)) {
+ *((dnode_t *)result) = node;
+ return (PROM_WALK_TERMINATE);
+ }
+ return (PROM_WALK_CONTINUE);
+}
+
+dnode_t
+prom_findnode_bydevtype(dnode_t node, char *devtype)
+{
+ dnode_t result = OBP_NONODE;
+ prom_walk_devs(node, bytype_cb, devtype, &result);
+ return (result);
+}
+
+
+/*
+ * prom_findnode_byname() searches the prom device subtree rooted at 'node' and
+ * returns the first node whose name matches the name supplied in 'name'.
+ */
+static int
+byname_cb(dnode_t node, void *arg, void *result)
+{
+ if (prom_getnode_byname(node, (char *)arg)) {
+ *((dnode_t *)result) = node;
+ return (PROM_WALK_TERMINATE);
+ }
+ return (PROM_WALK_CONTINUE);
+}
+
+dnode_t
+prom_findnode_byname(dnode_t node, char *name)
+{
+ dnode_t result = OBP_NONODE;
+ prom_walk_devs(node, byname_cb, name, &result);
+ return (result);
+}
+
+/*
+ * Return the root nodeid.
+ * Calling prom_nextnode(0) returns the root nodeid.
+ */
+dnode_t
+prom_rootnode(void)
+{
+ static dnode_t rootnode;
+
+ return (rootnode ? rootnode : (rootnode = prom_nextnode(OBP_NONODE)));
+}
+
+dnode_t
+prom_parentnode(dnode_t nodeid)
+{
+ cell_t ci[5];
+
+ ci[0] = p1275_ptr2cell("parent"); /* Service name */
+ ci[1] = (cell_t)1; /* #argument cells */
+ ci[2] = (cell_t)1; /* #result cells */
+ ci[3] = p1275_dnode2cell(nodeid); /* Arg1: input phandle */
+ ci[4] = p1275_dnode2cell(OBP_NONODE); /* Res1: Prime result */
+
+ promif_preprom();
+ (void) p1275_cif_handler(&ci);
+ promif_postprom();
+
+ return (p1275_cell2dnode(ci[4])); /* Res1: parent phandle */
+}
+
+dnode_t
+prom_finddevice(char *path)
+{
+ cell_t ci[5];
+#ifdef PROM_32BIT_ADDRS
+ char *opath = NULL;
+ size_t len;
+
+ if ((uintptr_t)path > (uint32_t)-1) {
+ opath = path;
+ len = prom_strlen(opath) + 1; /* include terminating NUL */
+ path = promplat_alloc(len);
+ if (path == NULL) {
+ return (OBP_BADNODE);
+ }
+ (void) prom_strcpy(path, opath);
+ }
+#endif
+
+ promif_preprom();
+
+ ci[0] = p1275_ptr2cell("finddevice"); /* Service name */
+ ci[1] = (cell_t)1; /* #argument cells */
+ ci[2] = (cell_t)1; /* #result cells */
+ ci[3] = p1275_ptr2cell(path); /* Arg1: pathname */
+ ci[4] = p1275_dnode2cell(OBP_BADNODE); /* Res1: Prime result */
+
+ (void) p1275_cif_handler(&ci);
+
+ promif_postprom();
+
+#ifdef PROM_32BIT_ADDRS
+ if (opath != NULL)
+ promplat_free(path, len);
+#endif
+
+ return ((dnode_t)p1275_cell2dnode(ci[4])); /* Res1: phandle */
+}
+
+dnode_t
+prom_chosennode(void)
+{
+ static dnode_t chosen;
+ dnode_t node;
+
+ if (chosen)
+ return (chosen);
+
+ node = prom_finddevice("/chosen");
+
+ if (node != OBP_BADNODE)
+ return (chosen = node);
+
+ prom_fatal_error("prom_chosennode: Can't find </chosen>\n");
+ /*NOTREACHED*/
+}
+
+/*
+ * Returns the nodeid of /aliases.
+ * /aliases exists in OBP >= 2.4 and in Open Firmware.
+ * Returns OBP_BADNODE if it doesn't exist.
+ */
+dnode_t
+prom_alias_node(void)
+{
+ static dnode_t node;
+
+ if (node == 0)
+ node = prom_finddevice("/aliases");
+ return (node);
+}
+
+/*
+ * Returns the nodeid of /options.
+ * Returns OBP_BADNODE if it doesn't exist.
+ */
+dnode_t
+prom_optionsnode(void)
+{
+ static dnode_t node;
+
+ if (node == 0)
+ node = prom_finddevice("/options");
+ return (node);
+}