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