10de71d50SPeter Tyser /* 20de71d50SPeter Tyser * libfdt - Flat Device Tree manipulation 30de71d50SPeter Tyser * Copyright (C) 2006 David Gibson, IBM Corporation. 435084760SRoger Meier * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause 50de71d50SPeter Tyser */ 6*6feed2a5SRobert P. J. Day #include <libfdt_env.h> 70de71d50SPeter Tyser 80de71d50SPeter Tyser #ifndef USE_HOSTCC 90de71d50SPeter Tyser #include <fdt.h> 100de71d50SPeter Tyser #include <libfdt.h> 110de71d50SPeter Tyser #else 120de71d50SPeter Tyser #include "fdt_host.h" 130de71d50SPeter Tyser #endif 140de71d50SPeter Tyser 150de71d50SPeter Tyser #include "libfdt_internal.h" 160de71d50SPeter Tyser 170de71d50SPeter Tyser static int _fdt_nodename_eq(const void *fdt, int offset, 180de71d50SPeter Tyser const char *s, int len) 190de71d50SPeter Tyser { 200de71d50SPeter Tyser const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1); 210de71d50SPeter Tyser 220de71d50SPeter Tyser if (!p) 230de71d50SPeter Tyser /* short match */ 240de71d50SPeter Tyser return 0; 250de71d50SPeter Tyser 260de71d50SPeter Tyser if (memcmp(p, s, len) != 0) 270de71d50SPeter Tyser return 0; 280de71d50SPeter Tyser 290de71d50SPeter Tyser if (p[len] == '\0') 300de71d50SPeter Tyser return 1; 310de71d50SPeter Tyser else if (!memchr(s, '@', len) && (p[len] == '@')) 320de71d50SPeter Tyser return 1; 330de71d50SPeter Tyser else 340de71d50SPeter Tyser return 0; 350de71d50SPeter Tyser } 360de71d50SPeter Tyser 370de71d50SPeter Tyser const char *fdt_string(const void *fdt, int stroffset) 380de71d50SPeter Tyser { 390de71d50SPeter Tyser return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; 400de71d50SPeter Tyser } 410de71d50SPeter Tyser 420de71d50SPeter Tyser static int _fdt_string_eq(const void *fdt, int stroffset, 430de71d50SPeter Tyser const char *s, int len) 440de71d50SPeter Tyser { 450de71d50SPeter Tyser const char *p = fdt_string(fdt, stroffset); 460de71d50SPeter Tyser 47af67b252SJon Nalley return (strnlen(p, len + 1) == len) && (memcmp(p, s, len) == 0); 480de71d50SPeter Tyser } 490de71d50SPeter Tyser 500de71d50SPeter Tyser int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) 510de71d50SPeter Tyser { 520de71d50SPeter Tyser FDT_CHECK_HEADER(fdt); 530de71d50SPeter Tyser *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address); 540de71d50SPeter Tyser *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size); 550de71d50SPeter Tyser return 0; 560de71d50SPeter Tyser } 570de71d50SPeter Tyser 580de71d50SPeter Tyser int fdt_num_mem_rsv(const void *fdt) 590de71d50SPeter Tyser { 600de71d50SPeter Tyser int i = 0; 610de71d50SPeter Tyser 620de71d50SPeter Tyser while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0) 630de71d50SPeter Tyser i++; 640de71d50SPeter Tyser return i; 650de71d50SPeter Tyser } 660de71d50SPeter Tyser 67d1c63148SDavid Gibson static int _nextprop(const void *fdt, int offset) 68d1c63148SDavid Gibson { 69d1c63148SDavid Gibson uint32_t tag; 70d1c63148SDavid Gibson int nextoffset; 71d1c63148SDavid Gibson 72d1c63148SDavid Gibson do { 73d1c63148SDavid Gibson tag = fdt_next_tag(fdt, offset, &nextoffset); 74d1c63148SDavid Gibson 75d1c63148SDavid Gibson switch (tag) { 76d1c63148SDavid Gibson case FDT_END: 77d1c63148SDavid Gibson if (nextoffset >= 0) 78d1c63148SDavid Gibson return -FDT_ERR_BADSTRUCTURE; 79d1c63148SDavid Gibson else 80d1c63148SDavid Gibson return nextoffset; 81d1c63148SDavid Gibson 82d1c63148SDavid Gibson case FDT_PROP: 83d1c63148SDavid Gibson return offset; 84d1c63148SDavid Gibson } 85d1c63148SDavid Gibson offset = nextoffset; 86d1c63148SDavid Gibson } while (tag == FDT_NOP); 87d1c63148SDavid Gibson 88d1c63148SDavid Gibson return -FDT_ERR_NOTFOUND; 89d1c63148SDavid Gibson } 90d1c63148SDavid Gibson 910de71d50SPeter Tyser int fdt_subnode_offset_namelen(const void *fdt, int offset, 920de71d50SPeter Tyser const char *name, int namelen) 930de71d50SPeter Tyser { 940de71d50SPeter Tyser int depth; 950de71d50SPeter Tyser 960de71d50SPeter Tyser FDT_CHECK_HEADER(fdt); 970de71d50SPeter Tyser 980de71d50SPeter Tyser for (depth = 0; 990de71d50SPeter Tyser (offset >= 0) && (depth >= 0); 1000de71d50SPeter Tyser offset = fdt_next_node(fdt, offset, &depth)) 1010de71d50SPeter Tyser if ((depth == 1) 1020de71d50SPeter Tyser && _fdt_nodename_eq(fdt, offset, name, namelen)) 1030de71d50SPeter Tyser return offset; 1040de71d50SPeter Tyser 1050de71d50SPeter Tyser if (depth < 0) 1060de71d50SPeter Tyser return -FDT_ERR_NOTFOUND; 1070de71d50SPeter Tyser return offset; /* error */ 1080de71d50SPeter Tyser } 1090de71d50SPeter Tyser 1100de71d50SPeter Tyser int fdt_subnode_offset(const void *fdt, int parentoffset, 1110de71d50SPeter Tyser const char *name) 1120de71d50SPeter Tyser { 1130de71d50SPeter Tyser return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); 1140de71d50SPeter Tyser } 1150de71d50SPeter Tyser 11677d7fff8SHans de Goede /* 11777d7fff8SHans de Goede * Find the next of path seperator, note we need to search for both '/' and ':' 118*6feed2a5SRobert P. J. Day * and then take the first one so that we do the right thing for e.g. 11977d7fff8SHans de Goede * "foo/bar:option" and "bar:option/otheroption", both of which happen, so 12077d7fff8SHans de Goede * first searching for either ':' or '/' does not work. 12177d7fff8SHans de Goede */ 12277d7fff8SHans de Goede static const char *fdt_path_next_seperator(const char *path) 12377d7fff8SHans de Goede { 12477d7fff8SHans de Goede const char *sep1 = strchr(path, '/'); 12577d7fff8SHans de Goede const char *sep2 = strchr(path, ':'); 12677d7fff8SHans de Goede 12777d7fff8SHans de Goede if (sep1 && sep2) 12877d7fff8SHans de Goede return (sep1 < sep2) ? sep1 : sep2; 12977d7fff8SHans de Goede else if (sep1) 13077d7fff8SHans de Goede return sep1; 13177d7fff8SHans de Goede else 13277d7fff8SHans de Goede return sep2; 13377d7fff8SHans de Goede } 13477d7fff8SHans de Goede 1350de71d50SPeter Tyser int fdt_path_offset(const void *fdt, const char *path) 1360de71d50SPeter Tyser { 1370de71d50SPeter Tyser const char *end = path + strlen(path); 1380de71d50SPeter Tyser const char *p = path; 1390de71d50SPeter Tyser int offset = 0; 1400de71d50SPeter Tyser 1410de71d50SPeter Tyser FDT_CHECK_HEADER(fdt); 1420de71d50SPeter Tyser 1430de71d50SPeter Tyser /* see if we have an alias */ 1440de71d50SPeter Tyser if (*path != '/') { 14577d7fff8SHans de Goede const char *q = fdt_path_next_seperator(path); 1460de71d50SPeter Tyser 1470de71d50SPeter Tyser if (!q) 1480de71d50SPeter Tyser q = end; 1490de71d50SPeter Tyser 1500de71d50SPeter Tyser p = fdt_get_alias_namelen(fdt, p, q - p); 1510de71d50SPeter Tyser if (!p) 1520de71d50SPeter Tyser return -FDT_ERR_BADPATH; 1530de71d50SPeter Tyser offset = fdt_path_offset(fdt, p); 1540de71d50SPeter Tyser 1550de71d50SPeter Tyser p = q; 1560de71d50SPeter Tyser } 1570de71d50SPeter Tyser 1580de71d50SPeter Tyser while (*p) { 1590de71d50SPeter Tyser const char *q; 1600de71d50SPeter Tyser 1610de71d50SPeter Tyser while (*p == '/') 1620de71d50SPeter Tyser p++; 16377d7fff8SHans de Goede if (*p == '\0' || *p == ':') 1640de71d50SPeter Tyser return offset; 16577d7fff8SHans de Goede q = fdt_path_next_seperator(p); 1660de71d50SPeter Tyser if (!q) 1670de71d50SPeter Tyser q = end; 1680de71d50SPeter Tyser 1690de71d50SPeter Tyser offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); 1700de71d50SPeter Tyser if (offset < 0) 1710de71d50SPeter Tyser return offset; 1720de71d50SPeter Tyser 1730de71d50SPeter Tyser p = q; 1740de71d50SPeter Tyser } 1750de71d50SPeter Tyser 1760de71d50SPeter Tyser return offset; 1770de71d50SPeter Tyser } 1780de71d50SPeter Tyser 1790de71d50SPeter Tyser const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) 1800de71d50SPeter Tyser { 1810de71d50SPeter Tyser const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset); 1820de71d50SPeter Tyser int err; 1830de71d50SPeter Tyser 1840de71d50SPeter Tyser if (((err = fdt_check_header(fdt)) != 0) 1850de71d50SPeter Tyser || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) 1860de71d50SPeter Tyser goto fail; 1870de71d50SPeter Tyser 1880de71d50SPeter Tyser if (len) 1890de71d50SPeter Tyser *len = strlen(nh->name); 1900de71d50SPeter Tyser 1910de71d50SPeter Tyser return nh->name; 1920de71d50SPeter Tyser 1930de71d50SPeter Tyser fail: 1940de71d50SPeter Tyser if (len) 1950de71d50SPeter Tyser *len = err; 1960de71d50SPeter Tyser return NULL; 1970de71d50SPeter Tyser } 1980de71d50SPeter Tyser 199d1c63148SDavid Gibson int fdt_first_property_offset(const void *fdt, int nodeoffset) 2000de71d50SPeter Tyser { 201d1c63148SDavid Gibson int offset; 202d1c63148SDavid Gibson 203d1c63148SDavid Gibson if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) 204d1c63148SDavid Gibson return offset; 205d1c63148SDavid Gibson 206d1c63148SDavid Gibson return _nextprop(fdt, offset); 207d1c63148SDavid Gibson } 208d1c63148SDavid Gibson 209d1c63148SDavid Gibson int fdt_next_property_offset(const void *fdt, int offset) 210d1c63148SDavid Gibson { 211d1c63148SDavid Gibson if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0) 212d1c63148SDavid Gibson return offset; 213d1c63148SDavid Gibson 214d1c63148SDavid Gibson return _nextprop(fdt, offset); 215d1c63148SDavid Gibson } 216d1c63148SDavid Gibson 217d1c63148SDavid Gibson const struct fdt_property *fdt_get_property_by_offset(const void *fdt, 218d1c63148SDavid Gibson int offset, 219d1c63148SDavid Gibson int *lenp) 220d1c63148SDavid Gibson { 2210de71d50SPeter Tyser int err; 222d1c63148SDavid Gibson const struct fdt_property *prop; 2230de71d50SPeter Tyser 224d1c63148SDavid Gibson if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) { 225d1c63148SDavid Gibson if (lenp) 226d1c63148SDavid Gibson *lenp = err; 227d1c63148SDavid Gibson return NULL; 228d1c63148SDavid Gibson } 2290de71d50SPeter Tyser 2300de71d50SPeter Tyser prop = _fdt_offset_ptr(fdt, offset); 231d1c63148SDavid Gibson 2320de71d50SPeter Tyser if (lenp) 2330de71d50SPeter Tyser *lenp = fdt32_to_cpu(prop->len); 2340de71d50SPeter Tyser 2350de71d50SPeter Tyser return prop; 2360de71d50SPeter Tyser } 237d1c63148SDavid Gibson 238d1c63148SDavid Gibson const struct fdt_property *fdt_get_property_namelen(const void *fdt, 239d1c63148SDavid Gibson int offset, 240d1c63148SDavid Gibson const char *name, 241d1c63148SDavid Gibson int namelen, int *lenp) 242d1c63148SDavid Gibson { 243d1c63148SDavid Gibson for (offset = fdt_first_property_offset(fdt, offset); 244d1c63148SDavid Gibson (offset >= 0); 245d1c63148SDavid Gibson (offset = fdt_next_property_offset(fdt, offset))) { 246d1c63148SDavid Gibson const struct fdt_property *prop; 247d1c63148SDavid Gibson 248d1c63148SDavid Gibson if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) { 249d1c63148SDavid Gibson offset = -FDT_ERR_INTERNAL; 2500de71d50SPeter Tyser break; 2510de71d50SPeter Tyser } 252d1c63148SDavid Gibson if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff), 253d1c63148SDavid Gibson name, namelen)) 254d1c63148SDavid Gibson return prop; 255d1c63148SDavid Gibson } 2560de71d50SPeter Tyser 2570de71d50SPeter Tyser if (lenp) 258d1c63148SDavid Gibson *lenp = offset; 2590de71d50SPeter Tyser return NULL; 2600de71d50SPeter Tyser } 2610de71d50SPeter Tyser 2620de71d50SPeter Tyser const struct fdt_property *fdt_get_property(const void *fdt, 2630de71d50SPeter Tyser int nodeoffset, 2640de71d50SPeter Tyser const char *name, int *lenp) 2650de71d50SPeter Tyser { 2660de71d50SPeter Tyser return fdt_get_property_namelen(fdt, nodeoffset, name, 2670de71d50SPeter Tyser strlen(name), lenp); 2680de71d50SPeter Tyser } 2690de71d50SPeter Tyser 2700de71d50SPeter Tyser const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, 2710de71d50SPeter Tyser const char *name, int namelen, int *lenp) 2720de71d50SPeter Tyser { 2730de71d50SPeter Tyser const struct fdt_property *prop; 2740de71d50SPeter Tyser 2750de71d50SPeter Tyser prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp); 2760de71d50SPeter Tyser if (!prop) 2770de71d50SPeter Tyser return NULL; 2780de71d50SPeter Tyser 2790de71d50SPeter Tyser return prop->data; 2800de71d50SPeter Tyser } 2810de71d50SPeter Tyser 282d1c63148SDavid Gibson const void *fdt_getprop_by_offset(const void *fdt, int offset, 283d1c63148SDavid Gibson const char **namep, int *lenp) 284d1c63148SDavid Gibson { 285d1c63148SDavid Gibson const struct fdt_property *prop; 286d1c63148SDavid Gibson 287d1c63148SDavid Gibson prop = fdt_get_property_by_offset(fdt, offset, lenp); 288d1c63148SDavid Gibson if (!prop) 289d1c63148SDavid Gibson return NULL; 290d1c63148SDavid Gibson if (namep) 291d1c63148SDavid Gibson *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); 292d1c63148SDavid Gibson return prop->data; 293d1c63148SDavid Gibson } 294d1c63148SDavid Gibson 2950de71d50SPeter Tyser const void *fdt_getprop(const void *fdt, int nodeoffset, 2960de71d50SPeter Tyser const char *name, int *lenp) 2970de71d50SPeter Tyser { 2980de71d50SPeter Tyser return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); 2990de71d50SPeter Tyser } 3000de71d50SPeter Tyser 3010de71d50SPeter Tyser uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) 3020de71d50SPeter Tyser { 303b2ba62a1SKim Phillips const fdt32_t *php; 3040de71d50SPeter Tyser int len; 3050de71d50SPeter Tyser 30605a22ba0SDavid Gibson /* FIXME: This is a bit sub-optimal, since we potentially scan 30705a22ba0SDavid Gibson * over all the properties twice. */ 30805a22ba0SDavid Gibson php = fdt_getprop(fdt, nodeoffset, "phandle", &len); 30905a22ba0SDavid Gibson if (!php || (len != sizeof(*php))) { 3100de71d50SPeter Tyser php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); 3110de71d50SPeter Tyser if (!php || (len != sizeof(*php))) 3120de71d50SPeter Tyser return 0; 31305a22ba0SDavid Gibson } 3140de71d50SPeter Tyser 3150de71d50SPeter Tyser return fdt32_to_cpu(*php); 3160de71d50SPeter Tyser } 3170de71d50SPeter Tyser 3180de71d50SPeter Tyser const char *fdt_get_alias_namelen(const void *fdt, 3190de71d50SPeter Tyser const char *name, int namelen) 3200de71d50SPeter Tyser { 3210de71d50SPeter Tyser int aliasoffset; 3220de71d50SPeter Tyser 3230de71d50SPeter Tyser aliasoffset = fdt_path_offset(fdt, "/aliases"); 3240de71d50SPeter Tyser if (aliasoffset < 0) 3250de71d50SPeter Tyser return NULL; 3260de71d50SPeter Tyser 3270de71d50SPeter Tyser return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); 3280de71d50SPeter Tyser } 3290de71d50SPeter Tyser 3300de71d50SPeter Tyser const char *fdt_get_alias(const void *fdt, const char *name) 3310de71d50SPeter Tyser { 3320de71d50SPeter Tyser return fdt_get_alias_namelen(fdt, name, strlen(name)); 3330de71d50SPeter Tyser } 3340de71d50SPeter Tyser 3350de71d50SPeter Tyser int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) 3360de71d50SPeter Tyser { 3370de71d50SPeter Tyser int pdepth = 0, p = 0; 3380de71d50SPeter Tyser int offset, depth, namelen; 3390de71d50SPeter Tyser const char *name; 3400de71d50SPeter Tyser 3410de71d50SPeter Tyser FDT_CHECK_HEADER(fdt); 3420de71d50SPeter Tyser 3430de71d50SPeter Tyser if (buflen < 2) 3440de71d50SPeter Tyser return -FDT_ERR_NOSPACE; 3450de71d50SPeter Tyser 3460de71d50SPeter Tyser for (offset = 0, depth = 0; 3470de71d50SPeter Tyser (offset >= 0) && (offset <= nodeoffset); 3480de71d50SPeter Tyser offset = fdt_next_node(fdt, offset, &depth)) { 3490de71d50SPeter Tyser while (pdepth > depth) { 3500de71d50SPeter Tyser do { 3510de71d50SPeter Tyser p--; 3520de71d50SPeter Tyser } while (buf[p-1] != '/'); 3530de71d50SPeter Tyser pdepth--; 3540de71d50SPeter Tyser } 3550de71d50SPeter Tyser 3560de71d50SPeter Tyser if (pdepth >= depth) { 3570de71d50SPeter Tyser name = fdt_get_name(fdt, offset, &namelen); 3580de71d50SPeter Tyser if (!name) 3590de71d50SPeter Tyser return namelen; 3600de71d50SPeter Tyser if ((p + namelen + 1) <= buflen) { 3610de71d50SPeter Tyser memcpy(buf + p, name, namelen); 3620de71d50SPeter Tyser p += namelen; 3630de71d50SPeter Tyser buf[p++] = '/'; 3640de71d50SPeter Tyser pdepth++; 3650de71d50SPeter Tyser } 3660de71d50SPeter Tyser } 3670de71d50SPeter Tyser 3680de71d50SPeter Tyser if (offset == nodeoffset) { 3690de71d50SPeter Tyser if (pdepth < (depth + 1)) 3700de71d50SPeter Tyser return -FDT_ERR_NOSPACE; 3710de71d50SPeter Tyser 3720de71d50SPeter Tyser if (p > 1) /* special case so that root path is "/", not "" */ 3730de71d50SPeter Tyser p--; 3740de71d50SPeter Tyser buf[p] = '\0'; 3750de71d50SPeter Tyser return 0; 3760de71d50SPeter Tyser } 3770de71d50SPeter Tyser } 3780de71d50SPeter Tyser 3790de71d50SPeter Tyser if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 3800de71d50SPeter Tyser return -FDT_ERR_BADOFFSET; 3810de71d50SPeter Tyser else if (offset == -FDT_ERR_BADOFFSET) 3820de71d50SPeter Tyser return -FDT_ERR_BADSTRUCTURE; 3830de71d50SPeter Tyser 3840de71d50SPeter Tyser return offset; /* error from fdt_next_node() */ 3850de71d50SPeter Tyser } 3860de71d50SPeter Tyser 3870de71d50SPeter Tyser int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, 3880de71d50SPeter Tyser int supernodedepth, int *nodedepth) 3890de71d50SPeter Tyser { 3900de71d50SPeter Tyser int offset, depth; 3910de71d50SPeter Tyser int supernodeoffset = -FDT_ERR_INTERNAL; 3920de71d50SPeter Tyser 3930de71d50SPeter Tyser FDT_CHECK_HEADER(fdt); 3940de71d50SPeter Tyser 3950de71d50SPeter Tyser if (supernodedepth < 0) 3960de71d50SPeter Tyser return -FDT_ERR_NOTFOUND; 3970de71d50SPeter Tyser 3980de71d50SPeter Tyser for (offset = 0, depth = 0; 3990de71d50SPeter Tyser (offset >= 0) && (offset <= nodeoffset); 4000de71d50SPeter Tyser offset = fdt_next_node(fdt, offset, &depth)) { 4010de71d50SPeter Tyser if (depth == supernodedepth) 4020de71d50SPeter Tyser supernodeoffset = offset; 4030de71d50SPeter Tyser 4040de71d50SPeter Tyser if (offset == nodeoffset) { 4050de71d50SPeter Tyser if (nodedepth) 4060de71d50SPeter Tyser *nodedepth = depth; 4070de71d50SPeter Tyser 4080de71d50SPeter Tyser if (supernodedepth > depth) 4090de71d50SPeter Tyser return -FDT_ERR_NOTFOUND; 4100de71d50SPeter Tyser else 4110de71d50SPeter Tyser return supernodeoffset; 4120de71d50SPeter Tyser } 4130de71d50SPeter Tyser } 4140de71d50SPeter Tyser 4150de71d50SPeter Tyser if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 4160de71d50SPeter Tyser return -FDT_ERR_BADOFFSET; 4170de71d50SPeter Tyser else if (offset == -FDT_ERR_BADOFFSET) 4180de71d50SPeter Tyser return -FDT_ERR_BADSTRUCTURE; 4190de71d50SPeter Tyser 4200de71d50SPeter Tyser return offset; /* error from fdt_next_node() */ 4210de71d50SPeter Tyser } 4220de71d50SPeter Tyser 4230de71d50SPeter Tyser int fdt_node_depth(const void *fdt, int nodeoffset) 4240de71d50SPeter Tyser { 4250de71d50SPeter Tyser int nodedepth; 4260de71d50SPeter Tyser int err; 4270de71d50SPeter Tyser 4280de71d50SPeter Tyser err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); 4290de71d50SPeter Tyser if (err) 4300de71d50SPeter Tyser return (err < 0) ? err : -FDT_ERR_INTERNAL; 4310de71d50SPeter Tyser return nodedepth; 4320de71d50SPeter Tyser } 4330de71d50SPeter Tyser 4340de71d50SPeter Tyser int fdt_parent_offset(const void *fdt, int nodeoffset) 4350de71d50SPeter Tyser { 4360de71d50SPeter Tyser int nodedepth = fdt_node_depth(fdt, nodeoffset); 4370de71d50SPeter Tyser 4380de71d50SPeter Tyser if (nodedepth < 0) 4390de71d50SPeter Tyser return nodedepth; 4400de71d50SPeter Tyser return fdt_supernode_atdepth_offset(fdt, nodeoffset, 4410de71d50SPeter Tyser nodedepth - 1, NULL); 4420de71d50SPeter Tyser } 4430de71d50SPeter Tyser 4440de71d50SPeter Tyser int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, 4450de71d50SPeter Tyser const char *propname, 4460de71d50SPeter Tyser const void *propval, int proplen) 4470de71d50SPeter Tyser { 4480de71d50SPeter Tyser int offset; 4490de71d50SPeter Tyser const void *val; 4500de71d50SPeter Tyser int len; 4510de71d50SPeter Tyser 4520de71d50SPeter Tyser FDT_CHECK_HEADER(fdt); 4530de71d50SPeter Tyser 4540de71d50SPeter Tyser /* FIXME: The algorithm here is pretty horrible: we scan each 4550de71d50SPeter Tyser * property of a node in fdt_getprop(), then if that didn't 4560de71d50SPeter Tyser * find what we want, we scan over them again making our way 4570de71d50SPeter Tyser * to the next node. Still it's the easiest to implement 4580de71d50SPeter Tyser * approach; performance can come later. */ 4590de71d50SPeter Tyser for (offset = fdt_next_node(fdt, startoffset, NULL); 4600de71d50SPeter Tyser offset >= 0; 4610de71d50SPeter Tyser offset = fdt_next_node(fdt, offset, NULL)) { 4620de71d50SPeter Tyser val = fdt_getprop(fdt, offset, propname, &len); 4630de71d50SPeter Tyser if (val && (len == proplen) 4640de71d50SPeter Tyser && (memcmp(val, propval, len) == 0)) 4650de71d50SPeter Tyser return offset; 4660de71d50SPeter Tyser } 4670de71d50SPeter Tyser 4680de71d50SPeter Tyser return offset; /* error from fdt_next_node() */ 4690de71d50SPeter Tyser } 4700de71d50SPeter Tyser 4710de71d50SPeter Tyser int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) 4720de71d50SPeter Tyser { 47305a22ba0SDavid Gibson int offset; 47405a22ba0SDavid Gibson 4750de71d50SPeter Tyser if ((phandle == 0) || (phandle == -1)) 4760de71d50SPeter Tyser return -FDT_ERR_BADPHANDLE; 47705a22ba0SDavid Gibson 47805a22ba0SDavid Gibson FDT_CHECK_HEADER(fdt); 47905a22ba0SDavid Gibson 48005a22ba0SDavid Gibson /* FIXME: The algorithm here is pretty horrible: we 48105a22ba0SDavid Gibson * potentially scan each property of a node in 48205a22ba0SDavid Gibson * fdt_get_phandle(), then if that didn't find what 48305a22ba0SDavid Gibson * we want, we scan over them again making our way to the next 48405a22ba0SDavid Gibson * node. Still it's the easiest to implement approach; 48505a22ba0SDavid Gibson * performance can come later. */ 48605a22ba0SDavid Gibson for (offset = fdt_next_node(fdt, -1, NULL); 48705a22ba0SDavid Gibson offset >= 0; 48805a22ba0SDavid Gibson offset = fdt_next_node(fdt, offset, NULL)) { 48905a22ba0SDavid Gibson if (fdt_get_phandle(fdt, offset) == phandle) 49005a22ba0SDavid Gibson return offset; 49105a22ba0SDavid Gibson } 49205a22ba0SDavid Gibson 49305a22ba0SDavid Gibson return offset; /* error from fdt_next_node() */ 4940de71d50SPeter Tyser } 4950de71d50SPeter Tyser 496e853b324SSimon Glass int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) 4970de71d50SPeter Tyser { 4980de71d50SPeter Tyser int len = strlen(str); 4990de71d50SPeter Tyser const char *p; 5000de71d50SPeter Tyser 5010de71d50SPeter Tyser while (listlen >= len) { 5020de71d50SPeter Tyser if (memcmp(str, strlist, len+1) == 0) 5030de71d50SPeter Tyser return 1; 5040de71d50SPeter Tyser p = memchr(strlist, '\0', listlen); 5050de71d50SPeter Tyser if (!p) 5060de71d50SPeter Tyser return 0; /* malformed strlist.. */ 5070de71d50SPeter Tyser listlen -= (p-strlist) + 1; 5080de71d50SPeter Tyser strlist = p + 1; 5090de71d50SPeter Tyser } 5100de71d50SPeter Tyser return 0; 5110de71d50SPeter Tyser } 5120de71d50SPeter Tyser 513bc4147abSThierry Reding int fdt_count_strings(const void *fdt, int node, const char *property) 514bc4147abSThierry Reding { 515bc4147abSThierry Reding int length, i, count = 0; 516bc4147abSThierry Reding const char *list; 517bc4147abSThierry Reding 518bc4147abSThierry Reding list = fdt_getprop(fdt, node, property, &length); 519bc4147abSThierry Reding if (!list) 52073e1e795SMasahiro Yamada return length; 521bc4147abSThierry Reding 522bc4147abSThierry Reding for (i = 0; i < length; i++) { 523bc4147abSThierry Reding int len = strlen(list); 524bc4147abSThierry Reding 525bc4147abSThierry Reding list += len + 1; 526bc4147abSThierry Reding i += len; 527bc4147abSThierry Reding count++; 528bc4147abSThierry Reding } 529bc4147abSThierry Reding 530bc4147abSThierry Reding return count; 531bc4147abSThierry Reding } 532bc4147abSThierry Reding 533fc503c17SThierry Reding int fdt_find_string(const void *fdt, int node, const char *property, 534fc503c17SThierry Reding const char *string) 535fc503c17SThierry Reding { 536fc503c17SThierry Reding const char *list, *end; 537fc503c17SThierry Reding int len, index = 0; 538fc503c17SThierry Reding 539fc503c17SThierry Reding list = fdt_getprop(fdt, node, property, &len); 540fc503c17SThierry Reding if (!list) 541fc503c17SThierry Reding return len; 542fc503c17SThierry Reding 543fc503c17SThierry Reding end = list + len; 544fc503c17SThierry Reding len = strlen(string); 545fc503c17SThierry Reding 546fc503c17SThierry Reding while (list < end) { 547fc503c17SThierry Reding int l = strlen(list); 548fc503c17SThierry Reding 549fc503c17SThierry Reding if (l == len && memcmp(list, string, len) == 0) 550fc503c17SThierry Reding return index; 551fc503c17SThierry Reding 552fc503c17SThierry Reding list += l + 1; 553fc503c17SThierry Reding index++; 554fc503c17SThierry Reding } 555fc503c17SThierry Reding 556fc503c17SThierry Reding return -FDT_ERR_NOTFOUND; 557fc503c17SThierry Reding } 558fc503c17SThierry Reding 5595094eb40SThierry Reding int fdt_get_string_index(const void *fdt, int node, const char *property, 5605094eb40SThierry Reding int index, const char **output) 5615094eb40SThierry Reding { 5625094eb40SThierry Reding const char *list; 5635094eb40SThierry Reding int length, i; 5645094eb40SThierry Reding 5655094eb40SThierry Reding list = fdt_getprop(fdt, node, property, &length); 5665094eb40SThierry Reding 5675094eb40SThierry Reding for (i = 0; i < length; i++) { 5685094eb40SThierry Reding int len = strlen(list); 5695094eb40SThierry Reding 5705094eb40SThierry Reding if (index == 0) { 5715094eb40SThierry Reding *output = list; 5725094eb40SThierry Reding return 0; 5735094eb40SThierry Reding } 5745094eb40SThierry Reding 5755094eb40SThierry Reding list += len + 1; 5765094eb40SThierry Reding i += len; 5775094eb40SThierry Reding index--; 5785094eb40SThierry Reding } 5795094eb40SThierry Reding 58031f334abSMasahiro Yamada return -FDT_ERR_NOTFOUND; 5815094eb40SThierry Reding } 5825094eb40SThierry Reding 5835094eb40SThierry Reding int fdt_get_string(const void *fdt, int node, const char *property, 5845094eb40SThierry Reding const char **output) 5855094eb40SThierry Reding { 5865094eb40SThierry Reding return fdt_get_string_index(fdt, node, property, 0, output); 5875094eb40SThierry Reding } 5885094eb40SThierry Reding 5890de71d50SPeter Tyser int fdt_node_check_compatible(const void *fdt, int nodeoffset, 5900de71d50SPeter Tyser const char *compatible) 5910de71d50SPeter Tyser { 5920de71d50SPeter Tyser const void *prop; 5930de71d50SPeter Tyser int len; 5940de71d50SPeter Tyser 5950de71d50SPeter Tyser prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); 5960de71d50SPeter Tyser if (!prop) 5970de71d50SPeter Tyser return len; 598e853b324SSimon Glass if (fdt_stringlist_contains(prop, len, compatible)) 5990de71d50SPeter Tyser return 0; 6000de71d50SPeter Tyser else 6010de71d50SPeter Tyser return 1; 6020de71d50SPeter Tyser } 6030de71d50SPeter Tyser 6040de71d50SPeter Tyser int fdt_node_offset_by_compatible(const void *fdt, int startoffset, 6050de71d50SPeter Tyser const char *compatible) 6060de71d50SPeter Tyser { 6070de71d50SPeter Tyser int offset, err; 6080de71d50SPeter Tyser 6090de71d50SPeter Tyser FDT_CHECK_HEADER(fdt); 6100de71d50SPeter Tyser 6110de71d50SPeter Tyser /* FIXME: The algorithm here is pretty horrible: we scan each 6120de71d50SPeter Tyser * property of a node in fdt_node_check_compatible(), then if 6130de71d50SPeter Tyser * that didn't find what we want, we scan over them again 6140de71d50SPeter Tyser * making our way to the next node. Still it's the easiest to 6150de71d50SPeter Tyser * implement approach; performance can come later. */ 6160de71d50SPeter Tyser for (offset = fdt_next_node(fdt, startoffset, NULL); 6170de71d50SPeter Tyser offset >= 0; 6180de71d50SPeter Tyser offset = fdt_next_node(fdt, offset, NULL)) { 6190de71d50SPeter Tyser err = fdt_node_check_compatible(fdt, offset, compatible); 6200de71d50SPeter Tyser if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) 6210de71d50SPeter Tyser return err; 6220de71d50SPeter Tyser else if (err == 0) 6230de71d50SPeter Tyser return offset; 6240de71d50SPeter Tyser } 6250de71d50SPeter Tyser 6260de71d50SPeter Tyser return offset; /* error from fdt_next_node() */ 6270de71d50SPeter Tyser } 628