162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * tree.c: Basic device tree traversal/scanning for the Linux 462306a36Sopenharmony_ci * prom library. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) 762306a36Sopenharmony_ci * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/string.h> 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/sched.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <asm/openprom.h> 1762306a36Sopenharmony_ci#include <asm/oplib.h> 1862306a36Sopenharmony_ci#include <asm/ldc.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic phandle prom_node_to_node(const char *type, phandle node) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci unsigned long args[5]; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci args[0] = (unsigned long) type; 2562306a36Sopenharmony_ci args[1] = 1; 2662306a36Sopenharmony_ci args[2] = 1; 2762306a36Sopenharmony_ci args[3] = (unsigned int) node; 2862306a36Sopenharmony_ci args[4] = (unsigned long) -1; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci p1275_cmd_direct(args); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci return (phandle) args[4]; 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* Return the child of node 'node' or zero if no this node has no 3662306a36Sopenharmony_ci * direct descendent. 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ciinline phandle __prom_getchild(phandle node) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci return prom_node_to_node("child", node); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ciphandle prom_getchild(phandle node) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci phandle cnode; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if ((s32)node == -1) 4862306a36Sopenharmony_ci return 0; 4962306a36Sopenharmony_ci cnode = __prom_getchild(node); 5062306a36Sopenharmony_ci if ((s32)cnode == -1) 5162306a36Sopenharmony_ci return 0; 5262306a36Sopenharmony_ci return cnode; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ciEXPORT_SYMBOL(prom_getchild); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ciinline phandle prom_getparent(phandle node) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci phandle cnode; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if ((s32)node == -1) 6162306a36Sopenharmony_ci return 0; 6262306a36Sopenharmony_ci cnode = prom_node_to_node("parent", node); 6362306a36Sopenharmony_ci if ((s32)cnode == -1) 6462306a36Sopenharmony_ci return 0; 6562306a36Sopenharmony_ci return cnode; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* Return the next sibling of node 'node' or zero if no more siblings 6962306a36Sopenharmony_ci * at this level of depth in the tree. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ciinline phandle __prom_getsibling(phandle node) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci return prom_node_to_node(prom_peer_name, node); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ciphandle prom_getsibling(phandle node) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci phandle sibnode; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if ((s32)node == -1) 8162306a36Sopenharmony_ci return 0; 8262306a36Sopenharmony_ci sibnode = __prom_getsibling(node); 8362306a36Sopenharmony_ci if ((s32)sibnode == -1) 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return sibnode; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ciEXPORT_SYMBOL(prom_getsibling); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* Return the length in bytes of property 'prop' at node 'node'. 9162306a36Sopenharmony_ci * Return -1 on error. 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ciint prom_getproplen(phandle node, const char *prop) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci unsigned long args[6]; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (!node || !prop) 9862306a36Sopenharmony_ci return -1; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci args[0] = (unsigned long) "getproplen"; 10162306a36Sopenharmony_ci args[1] = 2; 10262306a36Sopenharmony_ci args[2] = 1; 10362306a36Sopenharmony_ci args[3] = (unsigned int) node; 10462306a36Sopenharmony_ci args[4] = (unsigned long) prop; 10562306a36Sopenharmony_ci args[5] = (unsigned long) -1; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci p1275_cmd_direct(args); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return (int) args[5]; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ciEXPORT_SYMBOL(prom_getproplen); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* Acquire a property 'prop' at node 'node' and place it in 11462306a36Sopenharmony_ci * 'buffer' which has a size of 'bufsize'. If the acquisition 11562306a36Sopenharmony_ci * was successful the length will be returned, else -1 is returned. 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ciint prom_getproperty(phandle node, const char *prop, 11862306a36Sopenharmony_ci char *buffer, int bufsize) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci unsigned long args[8]; 12162306a36Sopenharmony_ci int plen; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci plen = prom_getproplen(node, prop); 12462306a36Sopenharmony_ci if ((plen > bufsize) || (plen == 0) || (plen == -1)) 12562306a36Sopenharmony_ci return -1; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci args[0] = (unsigned long) prom_getprop_name; 12862306a36Sopenharmony_ci args[1] = 4; 12962306a36Sopenharmony_ci args[2] = 1; 13062306a36Sopenharmony_ci args[3] = (unsigned int) node; 13162306a36Sopenharmony_ci args[4] = (unsigned long) prop; 13262306a36Sopenharmony_ci args[5] = (unsigned long) buffer; 13362306a36Sopenharmony_ci args[6] = bufsize; 13462306a36Sopenharmony_ci args[7] = (unsigned long) -1; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci p1275_cmd_direct(args); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return (int) args[7]; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ciEXPORT_SYMBOL(prom_getproperty); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* Acquire an integer property and return its value. Returns -1 14362306a36Sopenharmony_ci * on failure. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ciint prom_getint(phandle node, const char *prop) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci int intprop; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (prom_getproperty(node, prop, (char *) &intprop, sizeof(int)) != -1) 15062306a36Sopenharmony_ci return intprop; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return -1; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ciEXPORT_SYMBOL(prom_getint); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* Acquire an integer property, upon error return the passed default 15762306a36Sopenharmony_ci * integer. 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ciint prom_getintdefault(phandle node, const char *property, int deflt) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci int retval; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci retval = prom_getint(node, property); 16562306a36Sopenharmony_ci if (retval == -1) 16662306a36Sopenharmony_ci return deflt; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return retval; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ciEXPORT_SYMBOL(prom_getintdefault); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/* Acquire a boolean property, 1=TRUE 0=FALSE. */ 17362306a36Sopenharmony_ciint prom_getbool(phandle node, const char *prop) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci int retval; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci retval = prom_getproplen(node, prop); 17862306a36Sopenharmony_ci if (retval == -1) 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci return 1; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ciEXPORT_SYMBOL(prom_getbool); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/* Acquire a property whose value is a string, returns a null 18562306a36Sopenharmony_ci * string on error. The char pointer is the user supplied string 18662306a36Sopenharmony_ci * buffer. 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_civoid prom_getstring(phandle node, const char *prop, char *user_buf, 18962306a36Sopenharmony_ci int ubuf_size) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci int len; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci len = prom_getproperty(node, prop, user_buf, ubuf_size); 19462306a36Sopenharmony_ci if (len != -1) 19562306a36Sopenharmony_ci return; 19662306a36Sopenharmony_ci user_buf[0] = 0; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ciEXPORT_SYMBOL(prom_getstring); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci/* Does the device at node 'node' have name 'name'? 20162306a36Sopenharmony_ci * YES = 1 NO = 0 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_ciint prom_nodematch(phandle node, const char *name) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci char namebuf[128]; 20662306a36Sopenharmony_ci prom_getproperty(node, "name", namebuf, sizeof(namebuf)); 20762306a36Sopenharmony_ci if (strcmp(namebuf, name) == 0) 20862306a36Sopenharmony_ci return 1; 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci/* Search siblings at 'node_start' for a node with name 21362306a36Sopenharmony_ci * 'nodename'. Return node if successful, zero if not. 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ciphandle prom_searchsiblings(phandle node_start, const char *nodename) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci phandle thisnode; 21862306a36Sopenharmony_ci int error; 21962306a36Sopenharmony_ci char promlib_buf[128]; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci for(thisnode = node_start; thisnode; 22262306a36Sopenharmony_ci thisnode=prom_getsibling(thisnode)) { 22362306a36Sopenharmony_ci error = prom_getproperty(thisnode, "name", promlib_buf, 22462306a36Sopenharmony_ci sizeof(promlib_buf)); 22562306a36Sopenharmony_ci /* Should this ever happen? */ 22662306a36Sopenharmony_ci if(error == -1) continue; 22762306a36Sopenharmony_ci if(strcmp(nodename, promlib_buf)==0) return thisnode; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return 0; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ciEXPORT_SYMBOL(prom_searchsiblings); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic const char *prom_nextprop_name = "nextprop"; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/* Return the first property type for node 'node'. 23762306a36Sopenharmony_ci * buffer should be at least 32B in length 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_cichar *prom_firstprop(phandle node, char *buffer) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci unsigned long args[7]; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci *buffer = 0; 24462306a36Sopenharmony_ci if ((s32)node == -1) 24562306a36Sopenharmony_ci return buffer; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci args[0] = (unsigned long) prom_nextprop_name; 24862306a36Sopenharmony_ci args[1] = 3; 24962306a36Sopenharmony_ci args[2] = 1; 25062306a36Sopenharmony_ci args[3] = (unsigned int) node; 25162306a36Sopenharmony_ci args[4] = 0; 25262306a36Sopenharmony_ci args[5] = (unsigned long) buffer; 25362306a36Sopenharmony_ci args[6] = (unsigned long) -1; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci p1275_cmd_direct(args); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return buffer; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ciEXPORT_SYMBOL(prom_firstprop); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci/* Return the property type string after property type 'oprop' 26262306a36Sopenharmony_ci * at node 'node' . Returns NULL string if no more 26362306a36Sopenharmony_ci * property types for this node. 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_cichar *prom_nextprop(phandle node, const char *oprop, char *buffer) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci unsigned long args[7]; 26862306a36Sopenharmony_ci char buf[32]; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if ((s32)node == -1) { 27162306a36Sopenharmony_ci *buffer = 0; 27262306a36Sopenharmony_ci return buffer; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci if (oprop == buffer) { 27562306a36Sopenharmony_ci strcpy (buf, oprop); 27662306a36Sopenharmony_ci oprop = buf; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci args[0] = (unsigned long) prom_nextprop_name; 28062306a36Sopenharmony_ci args[1] = 3; 28162306a36Sopenharmony_ci args[2] = 1; 28262306a36Sopenharmony_ci args[3] = (unsigned int) node; 28362306a36Sopenharmony_ci args[4] = (unsigned long) oprop; 28462306a36Sopenharmony_ci args[5] = (unsigned long) buffer; 28562306a36Sopenharmony_ci args[6] = (unsigned long) -1; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci p1275_cmd_direct(args); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci return buffer; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ciEXPORT_SYMBOL(prom_nextprop); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ciphandle prom_finddevice(const char *name) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci unsigned long args[5]; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (!name) 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci args[0] = (unsigned long) "finddevice"; 30062306a36Sopenharmony_ci args[1] = 1; 30162306a36Sopenharmony_ci args[2] = 1; 30262306a36Sopenharmony_ci args[3] = (unsigned long) name; 30362306a36Sopenharmony_ci args[4] = (unsigned long) -1; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci p1275_cmd_direct(args); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return (int) args[4]; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ciEXPORT_SYMBOL(prom_finddevice); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ciint prom_node_has_property(phandle node, const char *prop) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci char buf [32]; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci *buf = 0; 31662306a36Sopenharmony_ci do { 31762306a36Sopenharmony_ci prom_nextprop(node, buf, buf); 31862306a36Sopenharmony_ci if (!strcmp(buf, prop)) 31962306a36Sopenharmony_ci return 1; 32062306a36Sopenharmony_ci } while (*buf); 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ciEXPORT_SYMBOL(prom_node_has_property); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci/* Set property 'pname' at node 'node' to value 'value' which has a length 32662306a36Sopenharmony_ci * of 'size' bytes. Return the number of bytes the prom accepted. 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_ciint 32962306a36Sopenharmony_ciprom_setprop(phandle node, const char *pname, char *value, int size) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci unsigned long args[8]; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (size == 0) 33462306a36Sopenharmony_ci return 0; 33562306a36Sopenharmony_ci if ((pname == 0) || (value == 0)) 33662306a36Sopenharmony_ci return 0; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci#ifdef CONFIG_SUN_LDOMS 33962306a36Sopenharmony_ci if (ldom_domaining_enabled) { 34062306a36Sopenharmony_ci ldom_set_var(pname, value); 34162306a36Sopenharmony_ci return 0; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci#endif 34462306a36Sopenharmony_ci args[0] = (unsigned long) "setprop"; 34562306a36Sopenharmony_ci args[1] = 4; 34662306a36Sopenharmony_ci args[2] = 1; 34762306a36Sopenharmony_ci args[3] = (unsigned int) node; 34862306a36Sopenharmony_ci args[4] = (unsigned long) pname; 34962306a36Sopenharmony_ci args[5] = (unsigned long) value; 35062306a36Sopenharmony_ci args[6] = size; 35162306a36Sopenharmony_ci args[7] = (unsigned long) -1; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci p1275_cmd_direct(args); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return (int) args[7]; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ciEXPORT_SYMBOL(prom_setprop); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ciinline phandle prom_inst2pkg(int inst) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci unsigned long args[5]; 36262306a36Sopenharmony_ci phandle node; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci args[0] = (unsigned long) "instance-to-package"; 36562306a36Sopenharmony_ci args[1] = 1; 36662306a36Sopenharmony_ci args[2] = 1; 36762306a36Sopenharmony_ci args[3] = (unsigned int) inst; 36862306a36Sopenharmony_ci args[4] = (unsigned long) -1; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci p1275_cmd_direct(args); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci node = (int) args[4]; 37362306a36Sopenharmony_ci if ((s32)node == -1) 37462306a36Sopenharmony_ci return 0; 37562306a36Sopenharmony_ci return node; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ciint prom_ihandle2path(int handle, char *buffer, int bufsize) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci unsigned long args[7]; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci args[0] = (unsigned long) "instance-to-path"; 38362306a36Sopenharmony_ci args[1] = 3; 38462306a36Sopenharmony_ci args[2] = 1; 38562306a36Sopenharmony_ci args[3] = (unsigned int) handle; 38662306a36Sopenharmony_ci args[4] = (unsigned long) buffer; 38762306a36Sopenharmony_ci args[5] = bufsize; 38862306a36Sopenharmony_ci args[6] = (unsigned long) -1; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci p1275_cmd_direct(args); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci return (int) args[6]; 39362306a36Sopenharmony_ci} 394