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