xref: /openbmc/u-boot/lib/libfdt/fdt_ro.c (revision 0de71d50)
1*0de71d50SPeter Tyser /*
2*0de71d50SPeter Tyser  * libfdt - Flat Device Tree manipulation
3*0de71d50SPeter Tyser  * Copyright (C) 2006 David Gibson, IBM Corporation.
4*0de71d50SPeter Tyser  *
5*0de71d50SPeter Tyser  * libfdt is dual licensed: you can use it either under the terms of
6*0de71d50SPeter Tyser  * the GPL, or the BSD license, at your option.
7*0de71d50SPeter Tyser  *
8*0de71d50SPeter Tyser  *  a) This library is free software; you can redistribute it and/or
9*0de71d50SPeter Tyser  *     modify it under the terms of the GNU General Public License as
10*0de71d50SPeter Tyser  *     published by the Free Software Foundation; either version 2 of the
11*0de71d50SPeter Tyser  *     License, or (at your option) any later version.
12*0de71d50SPeter Tyser  *
13*0de71d50SPeter Tyser  *     This library is distributed in the hope that it will be useful,
14*0de71d50SPeter Tyser  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
15*0de71d50SPeter Tyser  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16*0de71d50SPeter Tyser  *     GNU General Public License for more details.
17*0de71d50SPeter Tyser  *
18*0de71d50SPeter Tyser  *     You should have received a copy of the GNU General Public
19*0de71d50SPeter Tyser  *     License along with this library; if not, write to the Free
20*0de71d50SPeter Tyser  *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
21*0de71d50SPeter Tyser  *     MA 02110-1301 USA
22*0de71d50SPeter Tyser  *
23*0de71d50SPeter Tyser  * Alternatively,
24*0de71d50SPeter Tyser  *
25*0de71d50SPeter Tyser  *  b) Redistribution and use in source and binary forms, with or
26*0de71d50SPeter Tyser  *     without modification, are permitted provided that the following
27*0de71d50SPeter Tyser  *     conditions are met:
28*0de71d50SPeter Tyser  *
29*0de71d50SPeter Tyser  *     1. Redistributions of source code must retain the above
30*0de71d50SPeter Tyser  *        copyright notice, this list of conditions and the following
31*0de71d50SPeter Tyser  *        disclaimer.
32*0de71d50SPeter Tyser  *     2. Redistributions in binary form must reproduce the above
33*0de71d50SPeter Tyser  *        copyright notice, this list of conditions and the following
34*0de71d50SPeter Tyser  *        disclaimer in the documentation and/or other materials
35*0de71d50SPeter Tyser  *        provided with the distribution.
36*0de71d50SPeter Tyser  *
37*0de71d50SPeter Tyser  *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
38*0de71d50SPeter Tyser  *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
39*0de71d50SPeter Tyser  *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
40*0de71d50SPeter Tyser  *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
41*0de71d50SPeter Tyser  *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
42*0de71d50SPeter Tyser  *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43*0de71d50SPeter Tyser  *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44*0de71d50SPeter Tyser  *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45*0de71d50SPeter Tyser  *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46*0de71d50SPeter Tyser  *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
47*0de71d50SPeter Tyser  *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
48*0de71d50SPeter Tyser  *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
49*0de71d50SPeter Tyser  *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50*0de71d50SPeter Tyser  */
51*0de71d50SPeter Tyser #include "libfdt_env.h"
52*0de71d50SPeter Tyser 
53*0de71d50SPeter Tyser #ifndef USE_HOSTCC
54*0de71d50SPeter Tyser #include <fdt.h>
55*0de71d50SPeter Tyser #include <libfdt.h>
56*0de71d50SPeter Tyser #else
57*0de71d50SPeter Tyser #include "fdt_host.h"
58*0de71d50SPeter Tyser #endif
59*0de71d50SPeter Tyser 
60*0de71d50SPeter Tyser #include "libfdt_internal.h"
61*0de71d50SPeter Tyser 
62*0de71d50SPeter Tyser static int _fdt_nodename_eq(const void *fdt, int offset,
63*0de71d50SPeter Tyser 			    const char *s, int len)
64*0de71d50SPeter Tyser {
65*0de71d50SPeter Tyser 	const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
66*0de71d50SPeter Tyser 
67*0de71d50SPeter Tyser 	if (! p)
68*0de71d50SPeter Tyser 		/* short match */
69*0de71d50SPeter Tyser 		return 0;
70*0de71d50SPeter Tyser 
71*0de71d50SPeter Tyser 	if (memcmp(p, s, len) != 0)
72*0de71d50SPeter Tyser 		return 0;
73*0de71d50SPeter Tyser 
74*0de71d50SPeter Tyser 	if (p[len] == '\0')
75*0de71d50SPeter Tyser 		return 1;
76*0de71d50SPeter Tyser 	else if (!memchr(s, '@', len) && (p[len] == '@'))
77*0de71d50SPeter Tyser 		return 1;
78*0de71d50SPeter Tyser 	else
79*0de71d50SPeter Tyser 		return 0;
80*0de71d50SPeter Tyser }
81*0de71d50SPeter Tyser 
82*0de71d50SPeter Tyser const char *fdt_string(const void *fdt, int stroffset)
83*0de71d50SPeter Tyser {
84*0de71d50SPeter Tyser 	return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
85*0de71d50SPeter Tyser }
86*0de71d50SPeter Tyser 
87*0de71d50SPeter Tyser static int _fdt_string_eq(const void *fdt, int stroffset,
88*0de71d50SPeter Tyser 			  const char *s, int len)
89*0de71d50SPeter Tyser {
90*0de71d50SPeter Tyser 	const char *p = fdt_string(fdt, stroffset);
91*0de71d50SPeter Tyser 
92*0de71d50SPeter Tyser 	return (strlen(p) == len) && (memcmp(p, s, len) == 0);
93*0de71d50SPeter Tyser }
94*0de71d50SPeter Tyser 
95*0de71d50SPeter Tyser int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
96*0de71d50SPeter Tyser {
97*0de71d50SPeter Tyser 	FDT_CHECK_HEADER(fdt);
98*0de71d50SPeter Tyser 	*address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
99*0de71d50SPeter Tyser 	*size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
100*0de71d50SPeter Tyser 	return 0;
101*0de71d50SPeter Tyser }
102*0de71d50SPeter Tyser 
103*0de71d50SPeter Tyser int fdt_num_mem_rsv(const void *fdt)
104*0de71d50SPeter Tyser {
105*0de71d50SPeter Tyser 	int i = 0;
106*0de71d50SPeter Tyser 
107*0de71d50SPeter Tyser 	while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
108*0de71d50SPeter Tyser 		i++;
109*0de71d50SPeter Tyser 	return i;
110*0de71d50SPeter Tyser }
111*0de71d50SPeter Tyser 
112*0de71d50SPeter Tyser int fdt_subnode_offset_namelen(const void *fdt, int offset,
113*0de71d50SPeter Tyser 			       const char *name, int namelen)
114*0de71d50SPeter Tyser {
115*0de71d50SPeter Tyser 	int depth;
116*0de71d50SPeter Tyser 
117*0de71d50SPeter Tyser 	FDT_CHECK_HEADER(fdt);
118*0de71d50SPeter Tyser 
119*0de71d50SPeter Tyser 	for (depth = 0;
120*0de71d50SPeter Tyser 	     (offset >= 0) && (depth >= 0);
121*0de71d50SPeter Tyser 	     offset = fdt_next_node(fdt, offset, &depth))
122*0de71d50SPeter Tyser 		if ((depth == 1)
123*0de71d50SPeter Tyser 		    && _fdt_nodename_eq(fdt, offset, name, namelen))
124*0de71d50SPeter Tyser 			return offset;
125*0de71d50SPeter Tyser 
126*0de71d50SPeter Tyser 	if (depth < 0)
127*0de71d50SPeter Tyser 		return -FDT_ERR_NOTFOUND;
128*0de71d50SPeter Tyser 	return offset; /* error */
129*0de71d50SPeter Tyser }
130*0de71d50SPeter Tyser 
131*0de71d50SPeter Tyser int fdt_subnode_offset(const void *fdt, int parentoffset,
132*0de71d50SPeter Tyser 		       const char *name)
133*0de71d50SPeter Tyser {
134*0de71d50SPeter Tyser 	return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
135*0de71d50SPeter Tyser }
136*0de71d50SPeter Tyser 
137*0de71d50SPeter Tyser int fdt_path_offset(const void *fdt, const char *path)
138*0de71d50SPeter Tyser {
139*0de71d50SPeter Tyser 	const char *end = path + strlen(path);
140*0de71d50SPeter Tyser 	const char *p = path;
141*0de71d50SPeter Tyser 	int offset = 0;
142*0de71d50SPeter Tyser 
143*0de71d50SPeter Tyser 	FDT_CHECK_HEADER(fdt);
144*0de71d50SPeter Tyser 
145*0de71d50SPeter Tyser 	/* see if we have an alias */
146*0de71d50SPeter Tyser 	if (*path != '/') {
147*0de71d50SPeter Tyser 		const char *q = strchr(path, '/');
148*0de71d50SPeter Tyser 
149*0de71d50SPeter Tyser 		if (!q)
150*0de71d50SPeter Tyser 			q = end;
151*0de71d50SPeter Tyser 
152*0de71d50SPeter Tyser 		p = fdt_get_alias_namelen(fdt, p, q - p);
153*0de71d50SPeter Tyser 		if (!p)
154*0de71d50SPeter Tyser 			return -FDT_ERR_BADPATH;
155*0de71d50SPeter Tyser 		offset = fdt_path_offset(fdt, p);
156*0de71d50SPeter Tyser 
157*0de71d50SPeter Tyser 		p = q;
158*0de71d50SPeter Tyser 	}
159*0de71d50SPeter Tyser 
160*0de71d50SPeter Tyser 	while (*p) {
161*0de71d50SPeter Tyser 		const char *q;
162*0de71d50SPeter Tyser 
163*0de71d50SPeter Tyser 		while (*p == '/')
164*0de71d50SPeter Tyser 			p++;
165*0de71d50SPeter Tyser 		if (! *p)
166*0de71d50SPeter Tyser 			return offset;
167*0de71d50SPeter Tyser 		q = strchr(p, '/');
168*0de71d50SPeter Tyser 		if (! q)
169*0de71d50SPeter Tyser 			q = end;
170*0de71d50SPeter Tyser 
171*0de71d50SPeter Tyser 		offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
172*0de71d50SPeter Tyser 		if (offset < 0)
173*0de71d50SPeter Tyser 			return offset;
174*0de71d50SPeter Tyser 
175*0de71d50SPeter Tyser 		p = q;
176*0de71d50SPeter Tyser 	}
177*0de71d50SPeter Tyser 
178*0de71d50SPeter Tyser 	return offset;
179*0de71d50SPeter Tyser }
180*0de71d50SPeter Tyser 
181*0de71d50SPeter Tyser const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
182*0de71d50SPeter Tyser {
183*0de71d50SPeter Tyser 	const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
184*0de71d50SPeter Tyser 	int err;
185*0de71d50SPeter Tyser 
186*0de71d50SPeter Tyser 	if (((err = fdt_check_header(fdt)) != 0)
187*0de71d50SPeter Tyser 	    || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
188*0de71d50SPeter Tyser 			goto fail;
189*0de71d50SPeter Tyser 
190*0de71d50SPeter Tyser 	if (len)
191*0de71d50SPeter Tyser 		*len = strlen(nh->name);
192*0de71d50SPeter Tyser 
193*0de71d50SPeter Tyser 	return nh->name;
194*0de71d50SPeter Tyser 
195*0de71d50SPeter Tyser  fail:
196*0de71d50SPeter Tyser 	if (len)
197*0de71d50SPeter Tyser 		*len = err;
198*0de71d50SPeter Tyser 	return NULL;
199*0de71d50SPeter Tyser }
200*0de71d50SPeter Tyser 
201*0de71d50SPeter Tyser const struct fdt_property *fdt_get_property_namelen(const void *fdt,
202*0de71d50SPeter Tyser 						    int nodeoffset,
203*0de71d50SPeter Tyser 						    const char *name,
204*0de71d50SPeter Tyser 						    int namelen, int *lenp)
205*0de71d50SPeter Tyser {
206*0de71d50SPeter Tyser 	uint32_t tag;
207*0de71d50SPeter Tyser 	const struct fdt_property *prop;
208*0de71d50SPeter Tyser 	int offset, nextoffset;
209*0de71d50SPeter Tyser 	int err;
210*0de71d50SPeter Tyser 
211*0de71d50SPeter Tyser 	if (((err = fdt_check_header(fdt)) != 0)
212*0de71d50SPeter Tyser 	    || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
213*0de71d50SPeter Tyser 			goto fail;
214*0de71d50SPeter Tyser 
215*0de71d50SPeter Tyser 	nextoffset = err;
216*0de71d50SPeter Tyser 	do {
217*0de71d50SPeter Tyser 		offset = nextoffset;
218*0de71d50SPeter Tyser 
219*0de71d50SPeter Tyser 		tag = fdt_next_tag(fdt, offset, &nextoffset);
220*0de71d50SPeter Tyser 		switch (tag) {
221*0de71d50SPeter Tyser 		case FDT_END:
222*0de71d50SPeter Tyser 			if (nextoffset < 0)
223*0de71d50SPeter Tyser 				err = nextoffset;
224*0de71d50SPeter Tyser 			else
225*0de71d50SPeter Tyser 				/* FDT_END tag with unclosed nodes */
226*0de71d50SPeter Tyser 				err = -FDT_ERR_BADSTRUCTURE;
227*0de71d50SPeter Tyser 			goto fail;
228*0de71d50SPeter Tyser 
229*0de71d50SPeter Tyser 		case FDT_PROP:
230*0de71d50SPeter Tyser 			prop = _fdt_offset_ptr(fdt, offset);
231*0de71d50SPeter Tyser 			if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),
232*0de71d50SPeter Tyser 					   name, namelen)) {
233*0de71d50SPeter Tyser 				/* Found it! */
234*0de71d50SPeter Tyser 				if (lenp)
235*0de71d50SPeter Tyser 					*lenp = fdt32_to_cpu(prop->len);
236*0de71d50SPeter Tyser 
237*0de71d50SPeter Tyser 				return prop;
238*0de71d50SPeter Tyser 			}
239*0de71d50SPeter Tyser 			break;
240*0de71d50SPeter Tyser 		}
241*0de71d50SPeter Tyser 	} while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));
242*0de71d50SPeter Tyser 
243*0de71d50SPeter Tyser 	err = -FDT_ERR_NOTFOUND;
244*0de71d50SPeter Tyser  fail:
245*0de71d50SPeter Tyser 	if (lenp)
246*0de71d50SPeter Tyser 		*lenp = err;
247*0de71d50SPeter Tyser 	return NULL;
248*0de71d50SPeter Tyser }
249*0de71d50SPeter Tyser 
250*0de71d50SPeter Tyser const struct fdt_property *fdt_get_property(const void *fdt,
251*0de71d50SPeter Tyser 					    int nodeoffset,
252*0de71d50SPeter Tyser 					    const char *name, int *lenp)
253*0de71d50SPeter Tyser {
254*0de71d50SPeter Tyser 	return fdt_get_property_namelen(fdt, nodeoffset, name,
255*0de71d50SPeter Tyser 					strlen(name), lenp);
256*0de71d50SPeter Tyser }
257*0de71d50SPeter Tyser 
258*0de71d50SPeter Tyser const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
259*0de71d50SPeter Tyser 				const char *name, int namelen, int *lenp)
260*0de71d50SPeter Tyser {
261*0de71d50SPeter Tyser 	const struct fdt_property *prop;
262*0de71d50SPeter Tyser 
263*0de71d50SPeter Tyser 	prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
264*0de71d50SPeter Tyser 	if (! prop)
265*0de71d50SPeter Tyser 		return NULL;
266*0de71d50SPeter Tyser 
267*0de71d50SPeter Tyser 	return prop->data;
268*0de71d50SPeter Tyser }
269*0de71d50SPeter Tyser 
270*0de71d50SPeter Tyser const void *fdt_getprop(const void *fdt, int nodeoffset,
271*0de71d50SPeter Tyser 			const char *name, int *lenp)
272*0de71d50SPeter Tyser {
273*0de71d50SPeter Tyser 	return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
274*0de71d50SPeter Tyser }
275*0de71d50SPeter Tyser 
276*0de71d50SPeter Tyser uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
277*0de71d50SPeter Tyser {
278*0de71d50SPeter Tyser 	const uint32_t *php;
279*0de71d50SPeter Tyser 	int len;
280*0de71d50SPeter Tyser 
281*0de71d50SPeter Tyser 	php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
282*0de71d50SPeter Tyser 	if (!php || (len != sizeof(*php)))
283*0de71d50SPeter Tyser 		return 0;
284*0de71d50SPeter Tyser 
285*0de71d50SPeter Tyser 	return fdt32_to_cpu(*php);
286*0de71d50SPeter Tyser }
287*0de71d50SPeter Tyser 
288*0de71d50SPeter Tyser const char *fdt_get_alias_namelen(const void *fdt,
289*0de71d50SPeter Tyser 				  const char *name, int namelen)
290*0de71d50SPeter Tyser {
291*0de71d50SPeter Tyser 	int aliasoffset;
292*0de71d50SPeter Tyser 
293*0de71d50SPeter Tyser 	aliasoffset = fdt_path_offset(fdt, "/aliases");
294*0de71d50SPeter Tyser 	if (aliasoffset < 0)
295*0de71d50SPeter Tyser 		return NULL;
296*0de71d50SPeter Tyser 
297*0de71d50SPeter Tyser 	return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
298*0de71d50SPeter Tyser }
299*0de71d50SPeter Tyser 
300*0de71d50SPeter Tyser const char *fdt_get_alias(const void *fdt, const char *name)
301*0de71d50SPeter Tyser {
302*0de71d50SPeter Tyser 	return fdt_get_alias_namelen(fdt, name, strlen(name));
303*0de71d50SPeter Tyser }
304*0de71d50SPeter Tyser 
305*0de71d50SPeter Tyser int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
306*0de71d50SPeter Tyser {
307*0de71d50SPeter Tyser 	int pdepth = 0, p = 0;
308*0de71d50SPeter Tyser 	int offset, depth, namelen;
309*0de71d50SPeter Tyser 	const char *name;
310*0de71d50SPeter Tyser 
311*0de71d50SPeter Tyser 	FDT_CHECK_HEADER(fdt);
312*0de71d50SPeter Tyser 
313*0de71d50SPeter Tyser 	if (buflen < 2)
314*0de71d50SPeter Tyser 		return -FDT_ERR_NOSPACE;
315*0de71d50SPeter Tyser 
316*0de71d50SPeter Tyser 	for (offset = 0, depth = 0;
317*0de71d50SPeter Tyser 	     (offset >= 0) && (offset <= nodeoffset);
318*0de71d50SPeter Tyser 	     offset = fdt_next_node(fdt, offset, &depth)) {
319*0de71d50SPeter Tyser 		while (pdepth > depth) {
320*0de71d50SPeter Tyser 			do {
321*0de71d50SPeter Tyser 				p--;
322*0de71d50SPeter Tyser 			} while (buf[p-1] != '/');
323*0de71d50SPeter Tyser 			pdepth--;
324*0de71d50SPeter Tyser 		}
325*0de71d50SPeter Tyser 
326*0de71d50SPeter Tyser 		if (pdepth >= depth) {
327*0de71d50SPeter Tyser 			name = fdt_get_name(fdt, offset, &namelen);
328*0de71d50SPeter Tyser 			if (!name)
329*0de71d50SPeter Tyser 				return namelen;
330*0de71d50SPeter Tyser 			if ((p + namelen + 1) <= buflen) {
331*0de71d50SPeter Tyser 				memcpy(buf + p, name, namelen);
332*0de71d50SPeter Tyser 				p += namelen;
333*0de71d50SPeter Tyser 				buf[p++] = '/';
334*0de71d50SPeter Tyser 				pdepth++;
335*0de71d50SPeter Tyser 			}
336*0de71d50SPeter Tyser 		}
337*0de71d50SPeter Tyser 
338*0de71d50SPeter Tyser 		if (offset == nodeoffset) {
339*0de71d50SPeter Tyser 			if (pdepth < (depth + 1))
340*0de71d50SPeter Tyser 				return -FDT_ERR_NOSPACE;
341*0de71d50SPeter Tyser 
342*0de71d50SPeter Tyser 			if (p > 1) /* special case so that root path is "/", not "" */
343*0de71d50SPeter Tyser 				p--;
344*0de71d50SPeter Tyser 			buf[p] = '\0';
345*0de71d50SPeter Tyser 			return 0;
346*0de71d50SPeter Tyser 		}
347*0de71d50SPeter Tyser 	}
348*0de71d50SPeter Tyser 
349*0de71d50SPeter Tyser 	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
350*0de71d50SPeter Tyser 		return -FDT_ERR_BADOFFSET;
351*0de71d50SPeter Tyser 	else if (offset == -FDT_ERR_BADOFFSET)
352*0de71d50SPeter Tyser 		return -FDT_ERR_BADSTRUCTURE;
353*0de71d50SPeter Tyser 
354*0de71d50SPeter Tyser 	return offset; /* error from fdt_next_node() */
355*0de71d50SPeter Tyser }
356*0de71d50SPeter Tyser 
357*0de71d50SPeter Tyser int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
358*0de71d50SPeter Tyser 				 int supernodedepth, int *nodedepth)
359*0de71d50SPeter Tyser {
360*0de71d50SPeter Tyser 	int offset, depth;
361*0de71d50SPeter Tyser 	int supernodeoffset = -FDT_ERR_INTERNAL;
362*0de71d50SPeter Tyser 
363*0de71d50SPeter Tyser 	FDT_CHECK_HEADER(fdt);
364*0de71d50SPeter Tyser 
365*0de71d50SPeter Tyser 	if (supernodedepth < 0)
366*0de71d50SPeter Tyser 		return -FDT_ERR_NOTFOUND;
367*0de71d50SPeter Tyser 
368*0de71d50SPeter Tyser 	for (offset = 0, depth = 0;
369*0de71d50SPeter Tyser 	     (offset >= 0) && (offset <= nodeoffset);
370*0de71d50SPeter Tyser 	     offset = fdt_next_node(fdt, offset, &depth)) {
371*0de71d50SPeter Tyser 		if (depth == supernodedepth)
372*0de71d50SPeter Tyser 			supernodeoffset = offset;
373*0de71d50SPeter Tyser 
374*0de71d50SPeter Tyser 		if (offset == nodeoffset) {
375*0de71d50SPeter Tyser 			if (nodedepth)
376*0de71d50SPeter Tyser 				*nodedepth = depth;
377*0de71d50SPeter Tyser 
378*0de71d50SPeter Tyser 			if (supernodedepth > depth)
379*0de71d50SPeter Tyser 				return -FDT_ERR_NOTFOUND;
380*0de71d50SPeter Tyser 			else
381*0de71d50SPeter Tyser 				return supernodeoffset;
382*0de71d50SPeter Tyser 		}
383*0de71d50SPeter Tyser 	}
384*0de71d50SPeter Tyser 
385*0de71d50SPeter Tyser 	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
386*0de71d50SPeter Tyser 		return -FDT_ERR_BADOFFSET;
387*0de71d50SPeter Tyser 	else if (offset == -FDT_ERR_BADOFFSET)
388*0de71d50SPeter Tyser 		return -FDT_ERR_BADSTRUCTURE;
389*0de71d50SPeter Tyser 
390*0de71d50SPeter Tyser 	return offset; /* error from fdt_next_node() */
391*0de71d50SPeter Tyser }
392*0de71d50SPeter Tyser 
393*0de71d50SPeter Tyser int fdt_node_depth(const void *fdt, int nodeoffset)
394*0de71d50SPeter Tyser {
395*0de71d50SPeter Tyser 	int nodedepth;
396*0de71d50SPeter Tyser 	int err;
397*0de71d50SPeter Tyser 
398*0de71d50SPeter Tyser 	err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
399*0de71d50SPeter Tyser 	if (err)
400*0de71d50SPeter Tyser 		return (err < 0) ? err : -FDT_ERR_INTERNAL;
401*0de71d50SPeter Tyser 	return nodedepth;
402*0de71d50SPeter Tyser }
403*0de71d50SPeter Tyser 
404*0de71d50SPeter Tyser int fdt_parent_offset(const void *fdt, int nodeoffset)
405*0de71d50SPeter Tyser {
406*0de71d50SPeter Tyser 	int nodedepth = fdt_node_depth(fdt, nodeoffset);
407*0de71d50SPeter Tyser 
408*0de71d50SPeter Tyser 	if (nodedepth < 0)
409*0de71d50SPeter Tyser 		return nodedepth;
410*0de71d50SPeter Tyser 	return fdt_supernode_atdepth_offset(fdt, nodeoffset,
411*0de71d50SPeter Tyser 					    nodedepth - 1, NULL);
412*0de71d50SPeter Tyser }
413*0de71d50SPeter Tyser 
414*0de71d50SPeter Tyser int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
415*0de71d50SPeter Tyser 				  const char *propname,
416*0de71d50SPeter Tyser 				  const void *propval, int proplen)
417*0de71d50SPeter Tyser {
418*0de71d50SPeter Tyser 	int offset;
419*0de71d50SPeter Tyser 	const void *val;
420*0de71d50SPeter Tyser 	int len;
421*0de71d50SPeter Tyser 
422*0de71d50SPeter Tyser 	FDT_CHECK_HEADER(fdt);
423*0de71d50SPeter Tyser 
424*0de71d50SPeter Tyser 	/* FIXME: The algorithm here is pretty horrible: we scan each
425*0de71d50SPeter Tyser 	 * property of a node in fdt_getprop(), then if that didn't
426*0de71d50SPeter Tyser 	 * find what we want, we scan over them again making our way
427*0de71d50SPeter Tyser 	 * to the next node.  Still it's the easiest to implement
428*0de71d50SPeter Tyser 	 * approach; performance can come later. */
429*0de71d50SPeter Tyser 	for (offset = fdt_next_node(fdt, startoffset, NULL);
430*0de71d50SPeter Tyser 	     offset >= 0;
431*0de71d50SPeter Tyser 	     offset = fdt_next_node(fdt, offset, NULL)) {
432*0de71d50SPeter Tyser 		val = fdt_getprop(fdt, offset, propname, &len);
433*0de71d50SPeter Tyser 		if (val && (len == proplen)
434*0de71d50SPeter Tyser 		    && (memcmp(val, propval, len) == 0))
435*0de71d50SPeter Tyser 			return offset;
436*0de71d50SPeter Tyser 	}
437*0de71d50SPeter Tyser 
438*0de71d50SPeter Tyser 	return offset; /* error from fdt_next_node() */
439*0de71d50SPeter Tyser }
440*0de71d50SPeter Tyser 
441*0de71d50SPeter Tyser int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
442*0de71d50SPeter Tyser {
443*0de71d50SPeter Tyser 	if ((phandle == 0) || (phandle == -1))
444*0de71d50SPeter Tyser 		return -FDT_ERR_BADPHANDLE;
445*0de71d50SPeter Tyser 	phandle = cpu_to_fdt32(phandle);
446*0de71d50SPeter Tyser 	return fdt_node_offset_by_prop_value(fdt, -1, "linux,phandle",
447*0de71d50SPeter Tyser 					     &phandle, sizeof(phandle));
448*0de71d50SPeter Tyser }
449*0de71d50SPeter Tyser 
450*0de71d50SPeter Tyser static int _fdt_stringlist_contains(const char *strlist, int listlen,
451*0de71d50SPeter Tyser 				    const char *str)
452*0de71d50SPeter Tyser {
453*0de71d50SPeter Tyser 	int len = strlen(str);
454*0de71d50SPeter Tyser 	const char *p;
455*0de71d50SPeter Tyser 
456*0de71d50SPeter Tyser 	while (listlen >= len) {
457*0de71d50SPeter Tyser 		if (memcmp(str, strlist, len+1) == 0)
458*0de71d50SPeter Tyser 			return 1;
459*0de71d50SPeter Tyser 		p = memchr(strlist, '\0', listlen);
460*0de71d50SPeter Tyser 		if (!p)
461*0de71d50SPeter Tyser 			return 0; /* malformed strlist.. */
462*0de71d50SPeter Tyser 		listlen -= (p-strlist) + 1;
463*0de71d50SPeter Tyser 		strlist = p + 1;
464*0de71d50SPeter Tyser 	}
465*0de71d50SPeter Tyser 	return 0;
466*0de71d50SPeter Tyser }
467*0de71d50SPeter Tyser 
468*0de71d50SPeter Tyser int fdt_node_check_compatible(const void *fdt, int nodeoffset,
469*0de71d50SPeter Tyser 			      const char *compatible)
470*0de71d50SPeter Tyser {
471*0de71d50SPeter Tyser 	const void *prop;
472*0de71d50SPeter Tyser 	int len;
473*0de71d50SPeter Tyser 
474*0de71d50SPeter Tyser 	prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
475*0de71d50SPeter Tyser 	if (!prop)
476*0de71d50SPeter Tyser 		return len;
477*0de71d50SPeter Tyser 	if (_fdt_stringlist_contains(prop, len, compatible))
478*0de71d50SPeter Tyser 		return 0;
479*0de71d50SPeter Tyser 	else
480*0de71d50SPeter Tyser 		return 1;
481*0de71d50SPeter Tyser }
482*0de71d50SPeter Tyser 
483*0de71d50SPeter Tyser int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
484*0de71d50SPeter Tyser 				  const char *compatible)
485*0de71d50SPeter Tyser {
486*0de71d50SPeter Tyser 	int offset, err;
487*0de71d50SPeter Tyser 
488*0de71d50SPeter Tyser 	FDT_CHECK_HEADER(fdt);
489*0de71d50SPeter Tyser 
490*0de71d50SPeter Tyser 	/* FIXME: The algorithm here is pretty horrible: we scan each
491*0de71d50SPeter Tyser 	 * property of a node in fdt_node_check_compatible(), then if
492*0de71d50SPeter Tyser 	 * that didn't find what we want, we scan over them again
493*0de71d50SPeter Tyser 	 * making our way to the next node.  Still it's the easiest to
494*0de71d50SPeter Tyser 	 * implement approach; performance can come later. */
495*0de71d50SPeter Tyser 	for (offset = fdt_next_node(fdt, startoffset, NULL);
496*0de71d50SPeter Tyser 	     offset >= 0;
497*0de71d50SPeter Tyser 	     offset = fdt_next_node(fdt, offset, NULL)) {
498*0de71d50SPeter Tyser 		err = fdt_node_check_compatible(fdt, offset, compatible);
499*0de71d50SPeter Tyser 		if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
500*0de71d50SPeter Tyser 			return err;
501*0de71d50SPeter Tyser 		else if (err == 0)
502*0de71d50SPeter Tyser 			return offset;
503*0de71d50SPeter Tyser 	}
504*0de71d50SPeter Tyser 
505*0de71d50SPeter Tyser 	return offset; /* error from fdt_next_node() */
506*0de71d50SPeter Tyser }
507