diff options
| author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
| commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
| tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/sparc/prom/tree.c | |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/sparc/prom/tree.c')
| -rw-r--r-- | arch/sparc/prom/tree.c | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/arch/sparc/prom/tree.c b/arch/sparc/prom/tree.c new file mode 100644 index 000000000000..2bf03ee8cde5 --- /dev/null +++ b/arch/sparc/prom/tree.c | |||
| @@ -0,0 +1,364 @@ | |||
| 1 | /* $Id: tree.c,v 1.26 2000/08/26 02:38:03 anton Exp $ | ||
| 2 | * tree.c: Basic device tree traversal/scanning for the Linux | ||
| 3 | * prom library. | ||
| 4 | * | ||
| 5 | * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) | ||
| 6 | */ | ||
| 7 | |||
| 8 | #define PROMLIB_INTERNAL | ||
| 9 | |||
| 10 | #include <linux/string.h> | ||
| 11 | #include <linux/types.h> | ||
| 12 | #include <linux/kernel.h> | ||
| 13 | #include <linux/sched.h> | ||
| 14 | #include <linux/ctype.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 | int __prom_getchild(int node) | ||
| 25 | { | ||
| 26 | unsigned long flags; | ||
| 27 | int 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 | int prom_getchild(int node) | ||
| 41 | { | ||
| 42 | int cnode; | ||
| 43 | |||
| 44 | if (node == -1) | ||
| 45 | return 0; | ||
| 46 | |||
| 47 | cnode = __prom_getchild(node); | ||
| 48 | if (cnode == 0 || cnode == -1) | ||
| 49 | return 0; | ||
| 50 | |||
| 51 | return cnode; | ||
| 52 | } | ||
| 53 | |||
| 54 | /* Internal version of prom_getsibling that does not alter return values. */ | ||
| 55 | int __prom_getsibling(int node) | ||
| 56 | { | ||
| 57 | unsigned long flags; | ||
| 58 | int cnode; | ||
| 59 | |||
| 60 | spin_lock_irqsave(&prom_lock, flags); | ||
| 61 | cnode = prom_nodeops->no_nextnode(node); | ||
| 62 | restore_current(); | ||
| 63 | spin_unlock_irqrestore(&prom_lock, flags); | ||
| 64 | |||
| 65 | return cnode; | ||
| 66 | } | ||
| 67 | |||
| 68 | /* Return the next sibling of node 'node' or zero if no more siblings | ||
| 69 | * at this level of depth in the tree. | ||
| 70 | */ | ||
| 71 | int prom_getsibling(int node) | ||
| 72 | { | ||
| 73 | int sibnode; | ||
| 74 | |||
| 75 | if (node == -1) | ||
| 76 | return 0; | ||
| 77 | |||
| 78 | sibnode = __prom_getsibling(node); | ||
| 79 | if (sibnode == 0 || sibnode == -1) | ||
| 80 | return 0; | ||
| 81 | |||
| 82 | return sibnode; | ||
| 83 | } | ||
| 84 | |||
| 85 | /* Return the length in bytes of property 'prop' at node 'node'. | ||
| 86 | * Return -1 on error. | ||
| 87 | */ | ||
| 88 | int prom_getproplen(int node, char *prop) | ||
| 89 | { | ||
| 90 | int ret; | ||
| 91 | unsigned long flags; | ||
| 92 | |||
| 93 | if((!node) || (!prop)) | ||
| 94 | return -1; | ||
| 95 | |||
| 96 | spin_lock_irqsave(&prom_lock, flags); | ||
| 97 | ret = prom_nodeops->no_proplen(node, prop); | ||
| 98 | restore_current(); | ||
| 99 | spin_unlock_irqrestore(&prom_lock, flags); | ||
| 100 | return ret; | ||
| 101 | } | ||
| 102 | |||
| 103 | /* Acquire a property 'prop' at node 'node' and place it in | ||
| 104 | * 'buffer' which has a size of 'bufsize'. If the acquisition | ||
| 105 | * was successful the length will be returned, else -1 is returned. | ||
| 106 | */ | ||
| 107 | int prom_getproperty(int node, char *prop, char *buffer, int bufsize) | ||
| 108 | { | ||
| 109 | int plen, ret; | ||
| 110 | unsigned long flags; | ||
| 111 | |||
| 112 | plen = prom_getproplen(node, prop); | ||
| 113 | if((plen > bufsize) || (plen == 0) || (plen == -1)) | ||
| 114 | return -1; | ||
| 115 | /* Ok, things seem all right. */ | ||
| 116 | spin_lock_irqsave(&prom_lock, flags); | ||
| 117 | ret = prom_nodeops->no_getprop(node, prop, buffer); | ||
| 118 | restore_current(); | ||
| 119 | spin_unlock_irqrestore(&prom_lock, flags); | ||
| 120 | return ret; | ||
| 121 | } | ||
| 122 | |||
| 123 | /* Acquire an integer property and return its value. Returns -1 | ||
| 124 | * on failure. | ||
| 125 | */ | ||
| 126 | int prom_getint(int node, char *prop) | ||
| 127 | { | ||
| 128 | static int intprop; | ||
| 129 | |||
| 130 | if(prom_getproperty(node, prop, (char *) &intprop, sizeof(int)) != -1) | ||
| 131 | return intprop; | ||
| 132 | |||
| 133 | return -1; | ||
| 134 | } | ||
| 135 | |||
| 136 | /* Acquire an integer property, upon error return the passed default | ||
| 137 | * integer. | ||
| 138 | */ | ||
| 139 | int prom_getintdefault(int node, char *property, int deflt) | ||
| 140 | { | ||
| 141 | int retval; | ||
| 142 | |||
| 143 | retval = prom_getint(node, property); | ||
| 144 | if(retval == -1) return deflt; | ||
| 145 | |||
| 146 | return retval; | ||
| 147 | } | ||
| 148 | |||
| 149 | /* Acquire a boolean property, 1=TRUE 0=FALSE. */ | ||
| 150 | int prom_getbool(int node, char *prop) | ||
| 151 | { | ||
| 152 | int retval; | ||
| 153 | |||
| 154 | retval = prom_getproplen(node, prop); | ||
| 155 | if(retval == -1) return 0; | ||
| 156 | return 1; | ||
| 157 | } | ||
| 158 | |||
| 159 | /* Acquire a property whose value is a string, returns a null | ||
| 160 | * string on error. The char pointer is the user supplied string | ||
| 161 | * buffer. | ||
| 162 | */ | ||
| 163 | void prom_getstring(int node, char *prop, char *user_buf, int ubuf_size) | ||
| 164 | { | ||
| 165 | int len; | ||
| 166 | |||
| 167 | len = prom_getproperty(node, prop, user_buf, ubuf_size); | ||
| 168 | if(len != -1) return; | ||
| 169 | user_buf[0] = 0; | ||
| 170 | return; | ||
| 171 | } | ||
| 172 | |||
| 173 | |||
| 174 | /* Does the device at node 'node' have name 'name'? | ||
| 175 | * YES = 1 NO = 0 | ||
| 176 | */ | ||
| 177 | int prom_nodematch(int node, char *name) | ||
| 178 | { | ||
| 179 | int error; | ||
| 180 | |||
| 181 | static char namebuf[128]; | ||
| 182 | error = prom_getproperty(node, "name", namebuf, sizeof(namebuf)); | ||
| 183 | if (error == -1) return 0; | ||
| 184 | if(strcmp(namebuf, name) == 0) return 1; | ||
| 185 | return 0; | ||
| 186 | } | ||
| 187 | |||
| 188 | /* Search siblings at 'node_start' for a node with name | ||
| 189 | * 'nodename'. Return node if successful, zero if not. | ||
| 190 | */ | ||
| 191 | int prom_searchsiblings(int node_start, char *nodename) | ||
| 192 | { | ||
| 193 | |||
| 194 | int thisnode, error; | ||
| 195 | |||
| 196 | for(thisnode = node_start; thisnode; | ||
| 197 | thisnode=prom_getsibling(thisnode)) { | ||
| 198 | error = prom_getproperty(thisnode, "name", promlib_buf, | ||
| 199 | sizeof(promlib_buf)); | ||
| 200 | /* Should this ever happen? */ | ||
| 201 | if(error == -1) continue; | ||
| 202 | if(strcmp(nodename, promlib_buf)==0) return thisnode; | ||
| 203 | } | ||
| 204 | |||
| 205 | return 0; | ||
| 206 | } | ||
| 207 | |||
| 208 | /* Gets name in the form prom v2+ uses it (name@x,yyyyy or name (if no reg)) */ | ||
| 209 | int prom_getname (int node, char *buffer, int len) | ||
| 210 | { | ||
| 211 | int i; | ||
| 212 | struct linux_prom_registers reg[PROMREG_MAX]; | ||
| 213 | |||
| 214 | i = prom_getproperty (node, "name", buffer, len); | ||
| 215 | if (i <= 0) return -1; | ||
| 216 | buffer [i] = 0; | ||
| 217 | len -= i; | ||
| 218 | i = prom_getproperty (node, "reg", (char *)reg, sizeof (reg)); | ||
| 219 | if (i <= 0) return 0; | ||
| 220 | if (len < 11) return -1; | ||
| 221 | buffer = strchr (buffer, 0); | ||
| 222 | sprintf (buffer, "@%x,%x", reg[0].which_io, (uint)reg[0].phys_addr); | ||
| 223 | return 0; | ||
| 224 | } | ||
| 225 | |||
| 226 | /* Interal version of nextprop that does not alter return values. */ | ||
| 227 | char * __prom_nextprop(int node, char * oprop) | ||
| 228 | { | ||
| 229 | unsigned long flags; | ||
| 230 | char *prop; | ||
| 231 | |||
| 232 | spin_lock_irqsave(&prom_lock, flags); | ||
| 233 | prop = prom_nodeops->no_nextprop(node, oprop); | ||
| 234 | restore_current(); | ||
| 235 | spin_unlock_irqrestore(&prom_lock, flags); | ||
| 236 | |||
| 237 | return prop; | ||
| 238 | } | ||
| 239 | |||
| 240 | /* Return the first property name for node 'node'. */ | ||
| 241 | /* buffer is unused argument, but as v9 uses it, we need to have the same interface */ | ||
| 242 | char * prom_firstprop(int node, char *bufer) | ||
| 243 | { | ||
| 244 | if (node == 0 || node == -1) | ||
| 245 | return ""; | ||
| 246 | |||
| 247 | return __prom_nextprop(node, ""); | ||
| 248 | } | ||
| 249 | |||
| 250 | /* Return the property type string after property type 'oprop' | ||
| 251 | * at node 'node' . Returns empty string if no more | ||
| 252 | * property types for this node. | ||
| 253 | */ | ||
| 254 | char * prom_nextprop(int node, char *oprop, char *buffer) | ||
| 255 | { | ||
| 256 | if (node == 0 || node == -1) | ||
| 257 | return ""; | ||
| 258 | |||
| 259 | return __prom_nextprop(node, oprop); | ||
| 260 | } | ||
| 261 | |||
| 262 | int prom_finddevice(char *name) | ||
| 263 | { | ||
| 264 | char nbuf[128]; | ||
| 265 | char *s = name, *d; | ||
| 266 | int node = prom_root_node, node2; | ||
| 267 | unsigned int which_io, phys_addr; | ||
| 268 | struct linux_prom_registers reg[PROMREG_MAX]; | ||
| 269 | |||
| 270 | while (*s++) { | ||
| 271 | if (!*s) return node; /* path '.../' is legal */ | ||
| 272 | node = prom_getchild(node); | ||
| 273 | |||
| 274 | for (d = nbuf; *s != 0 && *s != '@' && *s != '/';) | ||
| 275 | *d++ = *s++; | ||
| 276 | *d = 0; | ||
| 277 | |||
| 278 | node = prom_searchsiblings(node, nbuf); | ||
| 279 | if (!node) | ||
| 280 | return 0; | ||
| 281 | |||
| 282 | if (*s == '@') { | ||
| 283 | if (isxdigit(s[1]) && s[2] == ',') { | ||
| 284 | which_io = simple_strtoul(s+1, NULL, 16); | ||
| 285 | phys_addr = simple_strtoul(s+3, &d, 16); | ||
| 286 | if (d != s + 3 && (!*d || *d == '/') | ||
| 287 | && d <= s + 3 + 8) { | ||
| 288 | node2 = node; | ||
| 289 | while (node2 && node2 != -1) { | ||
| 290 | if (prom_getproperty (node2, "reg", (char *)reg, sizeof (reg)) > 0) { | ||
| 291 | if (which_io == reg[0].which_io && phys_addr == reg[0].phys_addr) { | ||
| 292 | node = node2; | ||
| 293 | break; | ||
| 294 | } | ||
| 295 | } | ||
| 296 | node2 = prom_getsibling(node2); | ||
| 297 | if (!node2 || node2 == -1) | ||
| 298 | break; | ||
| 299 | node2 = prom_searchsiblings(prom_getsibling(node2), nbuf); | ||
| 300 | } | ||
| 301 | } | ||
| 302 | } | ||
| 303 | while (*s != 0 && *s != '/') s++; | ||
| 304 | } | ||
| 305 | } | ||
| 306 | return node; | ||
| 307 | } | ||
| 308 | |||
| 309 | int prom_node_has_property(int node, char *prop) | ||
| 310 | { | ||
| 311 | char *current_property = ""; | ||
| 312 | |||
| 313 | do { | ||
| 314 | current_property = prom_nextprop(node, current_property, NULL); | ||
| 315 | if(!strcmp(current_property, prop)) | ||
| 316 | return 1; | ||
| 317 | } while (*current_property); | ||
| 318 | return 0; | ||
| 319 | } | ||
| 320 | |||
| 321 | /* Set property 'pname' at node 'node' to value 'value' which has a length | ||
| 322 | * of 'size' bytes. Return the number of bytes the prom accepted. | ||
| 323 | */ | ||
| 324 | int prom_setprop(int node, char *pname, char *value, int size) | ||
| 325 | { | ||
| 326 | unsigned long flags; | ||
| 327 | int ret; | ||
| 328 | |||
| 329 | if(size == 0) return 0; | ||
| 330 | if((pname == 0) || (value == 0)) return 0; | ||
| 331 | spin_lock_irqsave(&prom_lock, flags); | ||
| 332 | ret = prom_nodeops->no_setprop(node, pname, value, size); | ||
| 333 | restore_current(); | ||
| 334 | spin_unlock_irqrestore(&prom_lock, flags); | ||
| 335 | return ret; | ||
| 336 | } | ||
| 337 | |||
| 338 | int prom_inst2pkg(int inst) | ||
| 339 | { | ||
| 340 | int node; | ||
| 341 | unsigned long flags; | ||
| 342 | |||
| 343 | spin_lock_irqsave(&prom_lock, flags); | ||
| 344 | node = (*romvec->pv_v2devops.v2_inst2pkg)(inst); | ||
| 345 | restore_current(); | ||
| 346 | spin_unlock_irqrestore(&prom_lock, flags); | ||
| 347 | if (node == -1) return 0; | ||
| 348 | return node; | ||
| 349 | } | ||
| 350 | |||
| 351 | /* Return 'node' assigned to a particular prom 'path' | ||
| 352 | * FIXME: Should work for v0 as well | ||
| 353 | */ | ||
| 354 | int prom_pathtoinode(char *path) | ||
| 355 | { | ||
| 356 | int node, inst; | ||
| 357 | |||
| 358 | inst = prom_devopen (path); | ||
| 359 | if (inst == -1) return 0; | ||
| 360 | node = prom_inst2pkg (inst); | ||
| 361 | prom_devclose (inst); | ||
| 362 | if (node == -1) return 0; | ||
| 363 | return node; | ||
| 364 | } | ||
