1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * tree.c: Basic device tree traversal/scanning for the Linux 4 * prom library. 5 * 6 * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) 7 */ 8 9 #include <linux/string.h> 10 #include <linux/types.h> 11 #include <linux/kernel.h> 12 #include <linux/sched.h> 13 #include <linux/ctype.h> 14 #include <linux/module.h> 15 16 #include <asm/openprom.h> 17 #include <asm/oplib.h> 18 19 extern void restore_current(void); 20 21 static char promlib_buf[128]; 22 23 /* Internal version of prom_getchild that does not alter return values. */ 24 static phandle __prom_getchild(phandle node) 25 { 26 unsigned long flags; 27 phandle cnode; 28 29 spin_lock_irqsave(&prom_lock, flags); 30 cnode = prom_nodeops->no_child(node); 31 restore_current(); 32 spin_unlock_irqrestore(&prom_lock, flags); 33 34 return cnode; 35 } 36 37 /* Return the child of node 'node' or zero if no this node has no 38 * direct descendent. 39 */ 40 phandle prom_getchild(phandle node) 41 { 42 phandle cnode; 43 44 if ((s32)node == -1) 45 return 0; 46 47 cnode = __prom_getchild(node); 48 if (cnode == 0 || (s32)cnode == -1) 49 return 0; 50 51 return cnode; 52 } 53 EXPORT_SYMBOL(prom_getchild); 54 55 /* Internal version of prom_getsibling that does not alter return values. */ 56 static phandle __prom_getsibling(phandle node) 57 { 58 unsigned long flags; 59 phandle cnode; 60 61 spin_lock_irqsave(&prom_lock, flags); 62 cnode = prom_nodeops->no_nextnode(node); 63 restore_current(); 64 spin_unlock_irqrestore(&prom_lock, flags); 65 66 return cnode; 67 } 68 69 /* Return the next sibling of node 'node' or zero if no more siblings 70 * at this level of depth in the tree. 71 */ 72 phandle prom_getsibling(phandle node) 73 { 74 phandle sibnode; 75 76 if ((s32)node == -1) 77 return 0; 78 79 sibnode = __prom_getsibling(node); 80 if (sibnode == 0 || (s32)sibnode == -1) 81 return 0; 82 83 return sibnode; 84 } 85 EXPORT_SYMBOL(prom_getsibling); 86 87 /* Return the length in bytes of property 'prop' at node 'node'. 88 * Return -1 on error. 89 */ 90 int prom_getproplen(phandle node, const char *prop) 91 { 92 int ret; 93 unsigned long flags; 94 95 if((!node) || (!prop)) 96 return -1; 97 98 spin_lock_irqsave(&prom_lock, flags); 99 ret = prom_nodeops->no_proplen(node, prop); 100 restore_current(); 101 spin_unlock_irqrestore(&prom_lock, flags); 102 return ret; 103 } 104 EXPORT_SYMBOL(prom_getproplen); 105 106 /* Acquire a property 'prop' at node 'node' and place it in 107 * 'buffer' which has a size of 'bufsize'. If the acquisition 108 * was successful the length will be returned, else -1 is returned. 109 */ 110 int prom_getproperty(phandle node, const char *prop, char *buffer, int bufsize) 111 { 112 int plen, ret; 113 unsigned long flags; 114 115 plen = prom_getproplen(node, prop); 116 if((plen > bufsize) || (plen == 0) || (plen == -1)) 117 return -1; 118 /* Ok, things seem all right. */ 119 spin_lock_irqsave(&prom_lock, flags); 120 ret = prom_nodeops->no_getprop(node, prop, buffer); 121 restore_current(); 122 spin_unlock_irqrestore(&prom_lock, flags); 123 return ret; 124 } 125 EXPORT_SYMBOL(prom_getproperty); 126 127 /* Acquire an integer property and return its value. Returns -1 128 * on failure. 129 */ 130 int prom_getint(phandle node, char *prop) 131 { 132 static int intprop; 133 134 if(prom_getproperty(node, prop, (char *) &intprop, sizeof(int)) != -1) 135 return intprop; 136 137 return -1; 138 } 139 EXPORT_SYMBOL(prom_getint); 140 141 /* Acquire an integer property, upon error return the passed default 142 * integer. 143 */ 144 int prom_getintdefault(phandle node, char *property, int deflt) 145 { 146 int retval; 147 148 retval = prom_getint(node, property); 149 if(retval == -1) return deflt; 150 151 return retval; 152 } 153 EXPORT_SYMBOL(prom_getintdefault); 154 155 /* Acquire a boolean property, 1=TRUE 0=FALSE. */ 156 int prom_getbool(phandle node, char *prop) 157 { 158 int retval; 159 160 retval = prom_getproplen(node, prop); 161 if(retval == -1) return 0; 162 return 1; 163 } 164 EXPORT_SYMBOL(prom_getbool); 165 166 /* Acquire a property whose value is a string, returns a null 167 * string on error. The char pointer is the user supplied string 168 * buffer. 169 */ 170 void prom_getstring(phandle node, char *prop, char *user_buf, int ubuf_size) 171 { 172 int len; 173 174 len = prom_getproperty(node, prop, user_buf, ubuf_size); 175 if(len != -1) return; 176 user_buf[0] = 0; 177 } 178 EXPORT_SYMBOL(prom_getstring); 179 180 181 /* Search siblings at 'node_start' for a node with name 182 * 'nodename'. Return node if successful, zero if not. 183 */ 184 phandle prom_searchsiblings(phandle node_start, char *nodename) 185 { 186 187 phandle thisnode; 188 int error; 189 190 for(thisnode = node_start; thisnode; 191 thisnode=prom_getsibling(thisnode)) { 192 error = prom_getproperty(thisnode, "name", promlib_buf, 193 sizeof(promlib_buf)); 194 /* Should this ever happen? */ 195 if(error == -1) continue; 196 if(strcmp(nodename, promlib_buf)==0) return thisnode; 197 } 198 199 return 0; 200 } 201 EXPORT_SYMBOL(prom_searchsiblings); 202 203 /* Interal version of nextprop that does not alter return values. */ 204 static char *__prom_nextprop(phandle node, char * oprop) 205 { 206 unsigned long flags; 207 char *prop; 208 209 spin_lock_irqsave(&prom_lock, flags); 210 prop = prom_nodeops->no_nextprop(node, oprop); 211 restore_current(); 212 spin_unlock_irqrestore(&prom_lock, flags); 213 214 return prop; 215 } 216 217 /* Return the property type string after property type 'oprop' 218 * at node 'node' . Returns empty string if no more 219 * property types for this node. 220 */ 221 char *prom_nextprop(phandle node, char *oprop, char *buffer) 222 { 223 if (node == 0 || (s32)node == -1) 224 return ""; 225 226 return __prom_nextprop(node, oprop); 227 } 228 EXPORT_SYMBOL(prom_nextprop); 229 230 phandle prom_finddevice(char *name) 231 { 232 char nbuf[128]; 233 char *s = name, *d; 234 phandle node = prom_root_node, node2; 235 unsigned int which_io, phys_addr; 236 struct linux_prom_registers reg[PROMREG_MAX]; 237 238 while (*s++) { 239 if (!*s) return node; /* path '.../' is legal */ 240 node = prom_getchild(node); 241 242 for (d = nbuf; *s != 0 && *s != '@' && *s != '/';) 243 *d++ = *s++; 244 *d = 0; 245 246 node = prom_searchsiblings(node, nbuf); 247 if (!node) 248 return 0; 249 250 if (*s == '@') { 251 if (isxdigit(s[1]) && s[2] == ',') { 252 which_io = simple_strtoul(s+1, NULL, 16); 253 phys_addr = simple_strtoul(s+3, &d, 16); 254 if (d != s + 3 && (!*d || *d == '/') 255 && d <= s + 3 + 8) { 256 node2 = node; 257 while (node2 && (s32)node2 != -1) { 258 if (prom_getproperty (node2, "reg", (char *)reg, sizeof (reg)) > 0) { 259 if (which_io == reg[0].which_io && phys_addr == reg[0].phys_addr) { 260 node = node2; 261 break; 262 } 263 } 264 node2 = prom_getsibling(node2); 265 if (!node2 || (s32)node2 == -1) 266 break; 267 node2 = prom_searchsiblings(prom_getsibling(node2), nbuf); 268 } 269 } 270 } 271 while (*s != 0 && *s != '/') s++; 272 } 273 } 274 return node; 275 } 276 EXPORT_SYMBOL(prom_finddevice); 277 278 /* Set property 'pname' at node 'node' to value 'value' which has a length 279 * of 'size' bytes. Return the number of bytes the prom accepted. 280 */ 281 int prom_setprop(phandle node, const char *pname, char *value, int size) 282 { 283 unsigned long flags; 284 int ret; 285 286 if (size == 0) 287 return 0; 288 if ((pname == NULL) || (value == NULL)) 289 return 0; 290 spin_lock_irqsave(&prom_lock, flags); 291 ret = prom_nodeops->no_setprop(node, pname, value, size); 292 restore_current(); 293 spin_unlock_irqrestore(&prom_lock, flags); 294 return ret; 295 } 296 EXPORT_SYMBOL(prom_setprop); 297 298 phandle prom_inst2pkg(int inst) 299 { 300 phandle node; 301 unsigned long flags; 302 303 spin_lock_irqsave(&prom_lock, flags); 304 node = (*romvec->pv_v2devops.v2_inst2pkg)(inst); 305 restore_current(); 306 spin_unlock_irqrestore(&prom_lock, flags); 307 if ((s32)node == -1) 308 return 0; 309 return node; 310 } 311