diff options
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 | } | ||