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 */ 60de71d50SPeter Tyser #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 1160de71d50SPeter Tyser int fdt_path_offset(const void *fdt, const char *path) 1170de71d50SPeter Tyser { 1180de71d50SPeter Tyser const char *end = path + strlen(path); 1190de71d50SPeter Tyser const char *p = path; 1200de71d50SPeter Tyser int offset = 0; 1210de71d50SPeter Tyser 1220de71d50SPeter Tyser FDT_CHECK_HEADER(fdt); 1230de71d50SPeter Tyser 1240de71d50SPeter Tyser /* see if we have an alias */ 1250de71d50SPeter Tyser if (*path != '/') { 1260de71d50SPeter Tyser const char *q = strchr(path, '/'); 1270de71d50SPeter Tyser 1280de71d50SPeter Tyser if (!q) 1290de71d50SPeter Tyser q = end; 1300de71d50SPeter Tyser 1310de71d50SPeter Tyser p = fdt_get_alias_namelen(fdt, p, q - p); 1320de71d50SPeter Tyser if (!p) 1330de71d50SPeter Tyser return -FDT_ERR_BADPATH; 1340de71d50SPeter Tyser offset = fdt_path_offset(fdt, p); 1350de71d50SPeter Tyser 1360de71d50SPeter Tyser p = q; 1370de71d50SPeter Tyser } 1380de71d50SPeter Tyser 1390de71d50SPeter Tyser while (*p) { 1400de71d50SPeter Tyser const char *q; 1410de71d50SPeter Tyser 1420de71d50SPeter Tyser while (*p == '/') 1430de71d50SPeter Tyser p++; 1440de71d50SPeter Tyser if (! *p) 1450de71d50SPeter Tyser return offset; 1460de71d50SPeter Tyser q = strchr(p, '/'); 1470de71d50SPeter Tyser if (! q) 1480de71d50SPeter Tyser q = end; 1490de71d50SPeter Tyser 1500de71d50SPeter Tyser offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); 1510de71d50SPeter Tyser if (offset < 0) 1520de71d50SPeter Tyser return offset; 1530de71d50SPeter Tyser 1540de71d50SPeter Tyser p = q; 1550de71d50SPeter Tyser } 1560de71d50SPeter Tyser 1570de71d50SPeter Tyser return offset; 1580de71d50SPeter Tyser } 1590de71d50SPeter Tyser 1600de71d50SPeter Tyser const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) 1610de71d50SPeter Tyser { 1620de71d50SPeter Tyser const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset); 1630de71d50SPeter Tyser int err; 1640de71d50SPeter Tyser 1650de71d50SPeter Tyser if (((err = fdt_check_header(fdt)) != 0) 1660de71d50SPeter Tyser || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) 1670de71d50SPeter Tyser goto fail; 1680de71d50SPeter Tyser 1690de71d50SPeter Tyser if (len) 1700de71d50SPeter Tyser *len = strlen(nh->name); 1710de71d50SPeter Tyser 1720de71d50SPeter Tyser return nh->name; 1730de71d50SPeter Tyser 1740de71d50SPeter Tyser fail: 1750de71d50SPeter Tyser if (len) 1760de71d50SPeter Tyser *len = err; 1770de71d50SPeter Tyser return NULL; 1780de71d50SPeter Tyser } 1790de71d50SPeter Tyser 180d1c63148SDavid Gibson int fdt_first_property_offset(const void *fdt, int nodeoffset) 1810de71d50SPeter Tyser { 182d1c63148SDavid Gibson int offset; 183d1c63148SDavid Gibson 184d1c63148SDavid Gibson if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) 185d1c63148SDavid Gibson return offset; 186d1c63148SDavid Gibson 187d1c63148SDavid Gibson return _nextprop(fdt, offset); 188d1c63148SDavid Gibson } 189d1c63148SDavid Gibson 190d1c63148SDavid Gibson int fdt_next_property_offset(const void *fdt, int offset) 191d1c63148SDavid Gibson { 192d1c63148SDavid Gibson if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0) 193d1c63148SDavid Gibson return offset; 194d1c63148SDavid Gibson 195d1c63148SDavid Gibson return _nextprop(fdt, offset); 196d1c63148SDavid Gibson } 197d1c63148SDavid Gibson 198d1c63148SDavid Gibson const struct fdt_property *fdt_get_property_by_offset(const void *fdt, 199d1c63148SDavid Gibson int offset, 200d1c63148SDavid Gibson int *lenp) 201d1c63148SDavid Gibson { 2020de71d50SPeter Tyser int err; 203d1c63148SDavid Gibson const struct fdt_property *prop; 2040de71d50SPeter Tyser 205d1c63148SDavid Gibson if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) { 206d1c63148SDavid Gibson if (lenp) 207d1c63148SDavid Gibson *lenp = err; 208d1c63148SDavid Gibson return NULL; 209d1c63148SDavid Gibson } 2100de71d50SPeter Tyser 2110de71d50SPeter Tyser prop = _fdt_offset_ptr(fdt, offset); 212d1c63148SDavid Gibson 2130de71d50SPeter Tyser if (lenp) 2140de71d50SPeter Tyser *lenp = fdt32_to_cpu(prop->len); 2150de71d50SPeter Tyser 2160de71d50SPeter Tyser return prop; 2170de71d50SPeter Tyser } 218d1c63148SDavid Gibson 219d1c63148SDavid Gibson const struct fdt_property *fdt_get_property_namelen(const void *fdt, 220d1c63148SDavid Gibson int offset, 221d1c63148SDavid Gibson const char *name, 222d1c63148SDavid Gibson int namelen, int *lenp) 223d1c63148SDavid Gibson { 224d1c63148SDavid Gibson for (offset = fdt_first_property_offset(fdt, offset); 225d1c63148SDavid Gibson (offset >= 0); 226d1c63148SDavid Gibson (offset = fdt_next_property_offset(fdt, offset))) { 227d1c63148SDavid Gibson const struct fdt_property *prop; 228d1c63148SDavid Gibson 229d1c63148SDavid Gibson if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) { 230d1c63148SDavid Gibson offset = -FDT_ERR_INTERNAL; 2310de71d50SPeter Tyser break; 2320de71d50SPeter Tyser } 233d1c63148SDavid Gibson if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff), 234d1c63148SDavid Gibson name, namelen)) 235d1c63148SDavid Gibson return prop; 236d1c63148SDavid Gibson } 2370de71d50SPeter Tyser 2380de71d50SPeter Tyser if (lenp) 239d1c63148SDavid Gibson *lenp = offset; 2400de71d50SPeter Tyser return NULL; 2410de71d50SPeter Tyser } 2420de71d50SPeter Tyser 2430de71d50SPeter Tyser const struct fdt_property *fdt_get_property(const void *fdt, 2440de71d50SPeter Tyser int nodeoffset, 2450de71d50SPeter Tyser const char *name, int *lenp) 2460de71d50SPeter Tyser { 2470de71d50SPeter Tyser return fdt_get_property_namelen(fdt, nodeoffset, name, 2480de71d50SPeter Tyser strlen(name), lenp); 2490de71d50SPeter Tyser } 2500de71d50SPeter Tyser 2510de71d50SPeter Tyser const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, 2520de71d50SPeter Tyser const char *name, int namelen, int *lenp) 2530de71d50SPeter Tyser { 2540de71d50SPeter Tyser const struct fdt_property *prop; 2550de71d50SPeter Tyser 2560de71d50SPeter Tyser prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp); 2570de71d50SPeter Tyser if (! prop) 2580de71d50SPeter Tyser return NULL; 2590de71d50SPeter Tyser 2600de71d50SPeter Tyser return prop->data; 2610de71d50SPeter Tyser } 2620de71d50SPeter Tyser 263d1c63148SDavid Gibson const void *fdt_getprop_by_offset(const void *fdt, int offset, 264d1c63148SDavid Gibson const char **namep, int *lenp) 265d1c63148SDavid Gibson { 266d1c63148SDavid Gibson const struct fdt_property *prop; 267d1c63148SDavid Gibson 268d1c63148SDavid Gibson prop = fdt_get_property_by_offset(fdt, offset, lenp); 269d1c63148SDavid Gibson if (!prop) 270d1c63148SDavid Gibson return NULL; 271d1c63148SDavid Gibson if (namep) 272d1c63148SDavid Gibson *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); 273d1c63148SDavid Gibson return prop->data; 274d1c63148SDavid Gibson } 275d1c63148SDavid Gibson 2760de71d50SPeter Tyser const void *fdt_getprop(const void *fdt, int nodeoffset, 2770de71d50SPeter Tyser const char *name, int *lenp) 2780de71d50SPeter Tyser { 2790de71d50SPeter Tyser return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); 2800de71d50SPeter Tyser } 2810de71d50SPeter Tyser 2820de71d50SPeter Tyser uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) 2830de71d50SPeter Tyser { 284b2ba62a1SKim Phillips const fdt32_t *php; 2850de71d50SPeter Tyser int len; 2860de71d50SPeter Tyser 28705a22ba0SDavid Gibson /* FIXME: This is a bit sub-optimal, since we potentially scan 28805a22ba0SDavid Gibson * over all the properties twice. */ 28905a22ba0SDavid Gibson php = fdt_getprop(fdt, nodeoffset, "phandle", &len); 29005a22ba0SDavid Gibson if (!php || (len != sizeof(*php))) { 2910de71d50SPeter Tyser php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); 2920de71d50SPeter Tyser if (!php || (len != sizeof(*php))) 2930de71d50SPeter Tyser return 0; 29405a22ba0SDavid Gibson } 2950de71d50SPeter Tyser 2960de71d50SPeter Tyser return fdt32_to_cpu(*php); 2970de71d50SPeter Tyser } 2980de71d50SPeter Tyser 2990de71d50SPeter Tyser const char *fdt_get_alias_namelen(const void *fdt, 3000de71d50SPeter Tyser const char *name, int namelen) 3010de71d50SPeter Tyser { 3020de71d50SPeter Tyser int aliasoffset; 3030de71d50SPeter Tyser 3040de71d50SPeter Tyser aliasoffset = fdt_path_offset(fdt, "/aliases"); 3050de71d50SPeter Tyser if (aliasoffset < 0) 3060de71d50SPeter Tyser return NULL; 3070de71d50SPeter Tyser 3080de71d50SPeter Tyser return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); 3090de71d50SPeter Tyser } 3100de71d50SPeter Tyser 3110de71d50SPeter Tyser const char *fdt_get_alias(const void *fdt, const char *name) 3120de71d50SPeter Tyser { 3130de71d50SPeter Tyser return fdt_get_alias_namelen(fdt, name, strlen(name)); 3140de71d50SPeter Tyser } 3150de71d50SPeter Tyser 3160de71d50SPeter Tyser int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) 3170de71d50SPeter Tyser { 3180de71d50SPeter Tyser int pdepth = 0, p = 0; 3190de71d50SPeter Tyser int offset, depth, namelen; 3200de71d50SPeter Tyser const char *name; 3210de71d50SPeter Tyser 3220de71d50SPeter Tyser FDT_CHECK_HEADER(fdt); 3230de71d50SPeter Tyser 3240de71d50SPeter Tyser if (buflen < 2) 3250de71d50SPeter Tyser return -FDT_ERR_NOSPACE; 3260de71d50SPeter Tyser 3270de71d50SPeter Tyser for (offset = 0, depth = 0; 3280de71d50SPeter Tyser (offset >= 0) && (offset <= nodeoffset); 3290de71d50SPeter Tyser offset = fdt_next_node(fdt, offset, &depth)) { 3300de71d50SPeter Tyser while (pdepth > depth) { 3310de71d50SPeter Tyser do { 3320de71d50SPeter Tyser p--; 3330de71d50SPeter Tyser } while (buf[p-1] != '/'); 3340de71d50SPeter Tyser pdepth--; 3350de71d50SPeter Tyser } 3360de71d50SPeter Tyser 3370de71d50SPeter Tyser if (pdepth >= depth) { 3380de71d50SPeter Tyser name = fdt_get_name(fdt, offset, &namelen); 3390de71d50SPeter Tyser if (!name) 3400de71d50SPeter Tyser return namelen; 3410de71d50SPeter Tyser if ((p + namelen + 1) <= buflen) { 3420de71d50SPeter Tyser memcpy(buf + p, name, namelen); 3430de71d50SPeter Tyser p += namelen; 3440de71d50SPeter Tyser buf[p++] = '/'; 3450de71d50SPeter Tyser pdepth++; 3460de71d50SPeter Tyser } 3470de71d50SPeter Tyser } 3480de71d50SPeter Tyser 3490de71d50SPeter Tyser if (offset == nodeoffset) { 3500de71d50SPeter Tyser if (pdepth < (depth + 1)) 3510de71d50SPeter Tyser return -FDT_ERR_NOSPACE; 3520de71d50SPeter Tyser 3530de71d50SPeter Tyser if (p > 1) /* special case so that root path is "/", not "" */ 3540de71d50SPeter Tyser p--; 3550de71d50SPeter Tyser buf[p] = '\0'; 3560de71d50SPeter Tyser return 0; 3570de71d50SPeter Tyser } 3580de71d50SPeter Tyser } 3590de71d50SPeter Tyser 3600de71d50SPeter Tyser if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 3610de71d50SPeter Tyser return -FDT_ERR_BADOFFSET; 3620de71d50SPeter Tyser else if (offset == -FDT_ERR_BADOFFSET) 3630de71d50SPeter Tyser return -FDT_ERR_BADSTRUCTURE; 3640de71d50SPeter Tyser 3650de71d50SPeter Tyser return offset; /* error from fdt_next_node() */ 3660de71d50SPeter Tyser } 3670de71d50SPeter Tyser 3680de71d50SPeter Tyser int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, 3690de71d50SPeter Tyser int supernodedepth, int *nodedepth) 3700de71d50SPeter Tyser { 3710de71d50SPeter Tyser int offset, depth; 3720de71d50SPeter Tyser int supernodeoffset = -FDT_ERR_INTERNAL; 3730de71d50SPeter Tyser 3740de71d50SPeter Tyser FDT_CHECK_HEADER(fdt); 3750de71d50SPeter Tyser 3760de71d50SPeter Tyser if (supernodedepth < 0) 3770de71d50SPeter Tyser return -FDT_ERR_NOTFOUND; 3780de71d50SPeter Tyser 3790de71d50SPeter Tyser for (offset = 0, depth = 0; 3800de71d50SPeter Tyser (offset >= 0) && (offset <= nodeoffset); 3810de71d50SPeter Tyser offset = fdt_next_node(fdt, offset, &depth)) { 3820de71d50SPeter Tyser if (depth == supernodedepth) 3830de71d50SPeter Tyser supernodeoffset = offset; 3840de71d50SPeter Tyser 3850de71d50SPeter Tyser if (offset == nodeoffset) { 3860de71d50SPeter Tyser if (nodedepth) 3870de71d50SPeter Tyser *nodedepth = depth; 3880de71d50SPeter Tyser 3890de71d50SPeter Tyser if (supernodedepth > depth) 3900de71d50SPeter Tyser return -FDT_ERR_NOTFOUND; 3910de71d50SPeter Tyser else 3920de71d50SPeter Tyser return supernodeoffset; 3930de71d50SPeter Tyser } 3940de71d50SPeter Tyser } 3950de71d50SPeter Tyser 3960de71d50SPeter Tyser if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 3970de71d50SPeter Tyser return -FDT_ERR_BADOFFSET; 3980de71d50SPeter Tyser else if (offset == -FDT_ERR_BADOFFSET) 3990de71d50SPeter Tyser return -FDT_ERR_BADSTRUCTURE; 4000de71d50SPeter Tyser 4010de71d50SPeter Tyser return offset; /* error from fdt_next_node() */ 4020de71d50SPeter Tyser } 4030de71d50SPeter Tyser 4040de71d50SPeter Tyser int fdt_node_depth(const void *fdt, int nodeoffset) 4050de71d50SPeter Tyser { 4060de71d50SPeter Tyser int nodedepth; 4070de71d50SPeter Tyser int err; 4080de71d50SPeter Tyser 4090de71d50SPeter Tyser err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); 4100de71d50SPeter Tyser if (err) 4110de71d50SPeter Tyser return (err < 0) ? err : -FDT_ERR_INTERNAL; 4120de71d50SPeter Tyser return nodedepth; 4130de71d50SPeter Tyser } 4140de71d50SPeter Tyser 4150de71d50SPeter Tyser int fdt_parent_offset(const void *fdt, int nodeoffset) 4160de71d50SPeter Tyser { 4170de71d50SPeter Tyser int nodedepth = fdt_node_depth(fdt, nodeoffset); 4180de71d50SPeter Tyser 4190de71d50SPeter Tyser if (nodedepth < 0) 4200de71d50SPeter Tyser return nodedepth; 4210de71d50SPeter Tyser return fdt_supernode_atdepth_offset(fdt, nodeoffset, 4220de71d50SPeter Tyser nodedepth - 1, NULL); 4230de71d50SPeter Tyser } 4240de71d50SPeter Tyser 4250de71d50SPeter Tyser int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, 4260de71d50SPeter Tyser const char *propname, 4270de71d50SPeter Tyser const void *propval, int proplen) 4280de71d50SPeter Tyser { 4290de71d50SPeter Tyser int offset; 4300de71d50SPeter Tyser const void *val; 4310de71d50SPeter Tyser int len; 4320de71d50SPeter Tyser 4330de71d50SPeter Tyser FDT_CHECK_HEADER(fdt); 4340de71d50SPeter Tyser 4350de71d50SPeter Tyser /* FIXME: The algorithm here is pretty horrible: we scan each 4360de71d50SPeter Tyser * property of a node in fdt_getprop(), then if that didn't 4370de71d50SPeter Tyser * find what we want, we scan over them again making our way 4380de71d50SPeter Tyser * to the next node. Still it's the easiest to implement 4390de71d50SPeter Tyser * approach; performance can come later. */ 4400de71d50SPeter Tyser for (offset = fdt_next_node(fdt, startoffset, NULL); 4410de71d50SPeter Tyser offset >= 0; 4420de71d50SPeter Tyser offset = fdt_next_node(fdt, offset, NULL)) { 4430de71d50SPeter Tyser val = fdt_getprop(fdt, offset, propname, &len); 4440de71d50SPeter Tyser if (val && (len == proplen) 4450de71d50SPeter Tyser && (memcmp(val, propval, len) == 0)) 4460de71d50SPeter Tyser return offset; 4470de71d50SPeter Tyser } 4480de71d50SPeter Tyser 4490de71d50SPeter Tyser return offset; /* error from fdt_next_node() */ 4500de71d50SPeter Tyser } 4510de71d50SPeter Tyser 4520de71d50SPeter Tyser int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) 4530de71d50SPeter Tyser { 45405a22ba0SDavid Gibson int offset; 45505a22ba0SDavid Gibson 4560de71d50SPeter Tyser if ((phandle == 0) || (phandle == -1)) 4570de71d50SPeter Tyser return -FDT_ERR_BADPHANDLE; 45805a22ba0SDavid Gibson 45905a22ba0SDavid Gibson FDT_CHECK_HEADER(fdt); 46005a22ba0SDavid Gibson 46105a22ba0SDavid Gibson /* FIXME: The algorithm here is pretty horrible: we 46205a22ba0SDavid Gibson * potentially scan each property of a node in 46305a22ba0SDavid Gibson * fdt_get_phandle(), then if that didn't find what 46405a22ba0SDavid Gibson * we want, we scan over them again making our way to the next 46505a22ba0SDavid Gibson * node. Still it's the easiest to implement approach; 46605a22ba0SDavid Gibson * performance can come later. */ 46705a22ba0SDavid Gibson for (offset = fdt_next_node(fdt, -1, NULL); 46805a22ba0SDavid Gibson offset >= 0; 46905a22ba0SDavid Gibson offset = fdt_next_node(fdt, offset, NULL)) { 47005a22ba0SDavid Gibson if (fdt_get_phandle(fdt, offset) == phandle) 47105a22ba0SDavid Gibson return offset; 47205a22ba0SDavid Gibson } 47305a22ba0SDavid Gibson 47405a22ba0SDavid Gibson return offset; /* error from fdt_next_node() */ 4750de71d50SPeter Tyser } 4760de71d50SPeter Tyser 477e853b324SSimon Glass int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) 4780de71d50SPeter Tyser { 4790de71d50SPeter Tyser int len = strlen(str); 4800de71d50SPeter Tyser const char *p; 4810de71d50SPeter Tyser 4820de71d50SPeter Tyser while (listlen >= len) { 4830de71d50SPeter Tyser if (memcmp(str, strlist, len+1) == 0) 4840de71d50SPeter Tyser return 1; 4850de71d50SPeter Tyser p = memchr(strlist, '\0', listlen); 4860de71d50SPeter Tyser if (!p) 4870de71d50SPeter Tyser return 0; /* malformed strlist.. */ 4880de71d50SPeter Tyser listlen -= (p-strlist) + 1; 4890de71d50SPeter Tyser strlist = p + 1; 4900de71d50SPeter Tyser } 4910de71d50SPeter Tyser return 0; 4920de71d50SPeter Tyser } 4930de71d50SPeter Tyser 494bc4147abSThierry Reding int fdt_count_strings(const void *fdt, int node, const char *property) 495bc4147abSThierry Reding { 496bc4147abSThierry Reding int length, i, count = 0; 497bc4147abSThierry Reding const char *list; 498bc4147abSThierry Reding 499bc4147abSThierry Reding list = fdt_getprop(fdt, node, property, &length); 500bc4147abSThierry Reding if (!list) 501bc4147abSThierry Reding return -length; 502bc4147abSThierry Reding 503bc4147abSThierry Reding for (i = 0; i < length; i++) { 504bc4147abSThierry Reding int len = strlen(list); 505bc4147abSThierry Reding 506bc4147abSThierry Reding list += len + 1; 507bc4147abSThierry Reding i += len; 508bc4147abSThierry Reding count++; 509bc4147abSThierry Reding } 510bc4147abSThierry Reding 511bc4147abSThierry Reding return count; 512bc4147abSThierry Reding } 513bc4147abSThierry Reding 514fc503c17SThierry Reding int fdt_find_string(const void *fdt, int node, const char *property, 515fc503c17SThierry Reding const char *string) 516fc503c17SThierry Reding { 517fc503c17SThierry Reding const char *list, *end; 518fc503c17SThierry Reding int len, index = 0; 519fc503c17SThierry Reding 520fc503c17SThierry Reding list = fdt_getprop(fdt, node, property, &len); 521fc503c17SThierry Reding if (!list) 522fc503c17SThierry Reding return len; 523fc503c17SThierry Reding 524fc503c17SThierry Reding end = list + len; 525fc503c17SThierry Reding len = strlen(string); 526fc503c17SThierry Reding 527fc503c17SThierry Reding while (list < end) { 528fc503c17SThierry Reding int l = strlen(list); 529fc503c17SThierry Reding 530fc503c17SThierry Reding if (l == len && memcmp(list, string, len) == 0) 531fc503c17SThierry Reding return index; 532fc503c17SThierry Reding 533fc503c17SThierry Reding list += l + 1; 534fc503c17SThierry Reding index++; 535fc503c17SThierry Reding } 536fc503c17SThierry Reding 537fc503c17SThierry Reding return -FDT_ERR_NOTFOUND; 538fc503c17SThierry Reding } 539fc503c17SThierry Reding 540*5094eb40SThierry Reding int fdt_get_string_index(const void *fdt, int node, const char *property, 541*5094eb40SThierry Reding int index, const char **output) 542*5094eb40SThierry Reding { 543*5094eb40SThierry Reding const char *list; 544*5094eb40SThierry Reding int length, i; 545*5094eb40SThierry Reding 546*5094eb40SThierry Reding list = fdt_getprop(fdt, node, property, &length); 547*5094eb40SThierry Reding 548*5094eb40SThierry Reding for (i = 0; i < length; i++) { 549*5094eb40SThierry Reding int len = strlen(list); 550*5094eb40SThierry Reding 551*5094eb40SThierry Reding if (index == 0) { 552*5094eb40SThierry Reding *output = list; 553*5094eb40SThierry Reding return 0; 554*5094eb40SThierry Reding } 555*5094eb40SThierry Reding 556*5094eb40SThierry Reding list += len + 1; 557*5094eb40SThierry Reding i += len; 558*5094eb40SThierry Reding index--; 559*5094eb40SThierry Reding } 560*5094eb40SThierry Reding 561*5094eb40SThierry Reding return FDT_ERR_NOTFOUND; 562*5094eb40SThierry Reding } 563*5094eb40SThierry Reding 564*5094eb40SThierry Reding int fdt_get_string(const void *fdt, int node, const char *property, 565*5094eb40SThierry Reding const char **output) 566*5094eb40SThierry Reding { 567*5094eb40SThierry Reding return fdt_get_string_index(fdt, node, property, 0, output); 568*5094eb40SThierry Reding } 569*5094eb40SThierry Reding 5700de71d50SPeter Tyser int fdt_node_check_compatible(const void *fdt, int nodeoffset, 5710de71d50SPeter Tyser const char *compatible) 5720de71d50SPeter Tyser { 5730de71d50SPeter Tyser const void *prop; 5740de71d50SPeter Tyser int len; 5750de71d50SPeter Tyser 5760de71d50SPeter Tyser prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); 5770de71d50SPeter Tyser if (!prop) 5780de71d50SPeter Tyser return len; 579e853b324SSimon Glass if (fdt_stringlist_contains(prop, len, compatible)) 5800de71d50SPeter Tyser return 0; 5810de71d50SPeter Tyser else 5820de71d50SPeter Tyser return 1; 5830de71d50SPeter Tyser } 5840de71d50SPeter Tyser 5850de71d50SPeter Tyser int fdt_node_offset_by_compatible(const void *fdt, int startoffset, 5860de71d50SPeter Tyser const char *compatible) 5870de71d50SPeter Tyser { 5880de71d50SPeter Tyser int offset, err; 5890de71d50SPeter Tyser 5900de71d50SPeter Tyser FDT_CHECK_HEADER(fdt); 5910de71d50SPeter Tyser 5920de71d50SPeter Tyser /* FIXME: The algorithm here is pretty horrible: we scan each 5930de71d50SPeter Tyser * property of a node in fdt_node_check_compatible(), then if 5940de71d50SPeter Tyser * that didn't find what we want, we scan over them again 5950de71d50SPeter Tyser * making our way to the next node. Still it's the easiest to 5960de71d50SPeter Tyser * implement approach; performance can come later. */ 5970de71d50SPeter Tyser for (offset = fdt_next_node(fdt, startoffset, NULL); 5980de71d50SPeter Tyser offset >= 0; 5990de71d50SPeter Tyser offset = fdt_next_node(fdt, offset, NULL)) { 6000de71d50SPeter Tyser err = fdt_node_check_compatible(fdt, offset, compatible); 6010de71d50SPeter Tyser if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) 6020de71d50SPeter Tyser return err; 6030de71d50SPeter Tyser else if (err == 0) 6040de71d50SPeter Tyser return offset; 6050de71d50SPeter Tyser } 6060de71d50SPeter Tyser 6070de71d50SPeter Tyser return offset; /* error from fdt_next_node() */ 6080de71d50SPeter Tyser } 609