1*3fe5dae2Skettenis /* $OpenBSD: fdt.c,v 1.35 2024/03/27 23:05:27 kettenis Exp $ */
2c03f87daSbmercer
3c03f87daSbmercer /*
4c03f87daSbmercer * Copyright (c) 2009 Dariusz Swiderski <sfires@sfires.net>
5b244cb8bSkettenis * Copyright (c) 2009 Mark Kettenis <kettenis@openbsd.org>
6c03f87daSbmercer *
7c03f87daSbmercer * Permission to use, copy, modify, and distribute this software for any
8c03f87daSbmercer * purpose with or without fee is hereby granted, provided that the above
9c03f87daSbmercer * copyright notice and this permission notice appear in all copies.
10c03f87daSbmercer *
11c03f87daSbmercer * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12c03f87daSbmercer * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13c03f87daSbmercer * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14c03f87daSbmercer * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15c03f87daSbmercer * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16c03f87daSbmercer * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17c03f87daSbmercer * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18c03f87daSbmercer */
19c03f87daSbmercer
20c03f87daSbmercer #include <sys/types.h>
21c03f87daSbmercer #include <sys/systm.h>
2250946bcbSkettenis #include <sys/malloc.h>
23c03f87daSbmercer
24c03f87daSbmercer #include <dev/ofw/fdt.h>
25c03f87daSbmercer #include <dev/ofw/openfirm.h>
26c03f87daSbmercer
27c03f87daSbmercer unsigned int fdt_check_head(void *);
28c03f87daSbmercer char *fdt_get_str(u_int32_t);
29c03f87daSbmercer void *skip_property(u_int32_t *);
30c03f87daSbmercer void *skip_props(u_int32_t *);
31c03f87daSbmercer void *skip_node_name(u_int32_t *);
32dd86c6a0Spatrick void *skip_node(void *);
33808ecccaSvisa void *skip_nops(u_int32_t *);
34c03f87daSbmercer void *fdt_parent_node_recurse(void *, void *);
35d0cd2b3aSkettenis void *fdt_find_phandle_recurse(void *, uint32_t);
36401e79efSpatrick int fdt_node_property_int(void *, char *, int *);
37401e79efSpatrick int fdt_node_property_ints(void *, char *, int *, int);
38b7952965Spatrick int fdt_translate_reg(void *, struct fdt_reg *);
39c03f87daSbmercer #ifdef DEBUG
40c03f87daSbmercer void fdt_print_node_recurse(void *, int);
41c03f87daSbmercer #endif
42c03f87daSbmercer
43c03f87daSbmercer static int tree_inited = 0;
44c03f87daSbmercer static struct fdt tree;
45c03f87daSbmercer
46c03f87daSbmercer unsigned int
fdt_check_head(void * fdt)47c03f87daSbmercer fdt_check_head(void *fdt)
48c03f87daSbmercer {
49c03f87daSbmercer struct fdt_head *fh;
50808ecccaSvisa u_int32_t *ptr, *tok;
51c03f87daSbmercer
52c03f87daSbmercer fh = fdt;
53c03f87daSbmercer ptr = (u_int32_t *)fdt;
54c03f87daSbmercer
55c03f87daSbmercer if (betoh32(fh->fh_magic) != FDT_MAGIC)
56c03f87daSbmercer return 0;
57c03f87daSbmercer
58c03f87daSbmercer if (betoh32(fh->fh_version) > FDT_CODE_VERSION)
59c03f87daSbmercer return 0;
60c03f87daSbmercer
61808ecccaSvisa tok = skip_nops(ptr + (betoh32(fh->fh_struct_off) / 4));
62808ecccaSvisa if (betoh32(*tok) != FDT_NODE_BEGIN)
63c03f87daSbmercer return 0;
64c03f87daSbmercer
65c03f87daSbmercer /* check for end signature on version 17 blob */
66c03f87daSbmercer if ((betoh32(fh->fh_version) >= 17) &&
673622bda5Smpi (betoh32(*(ptr + (betoh32(fh->fh_struct_off) / 4) +
683622bda5Smpi (betoh32(fh->fh_struct_size) / 4) - 1)) != FDT_END))
69c03f87daSbmercer return 0;
70c03f87daSbmercer
71c03f87daSbmercer return betoh32(fh->fh_version);
72c03f87daSbmercer }
73c03f87daSbmercer
74c03f87daSbmercer /*
75c03f87daSbmercer * Initializes internal structures of module.
76c03f87daSbmercer * Has to be called once, preferably in machdep.c.
77c03f87daSbmercer */
78c03f87daSbmercer int
fdt_init(void * fdt)79c03f87daSbmercer fdt_init(void *fdt)
80c03f87daSbmercer {
81c03f87daSbmercer int version;
82c03f87daSbmercer
83c03f87daSbmercer bzero(&tree, sizeof(struct fdt));
84c03f87daSbmercer tree_inited = 0;
85c03f87daSbmercer
86c03f87daSbmercer if (!fdt)
87c03f87daSbmercer return 0;
88c03f87daSbmercer
89c03f87daSbmercer if (!(version = fdt_check_head(fdt)))
90c03f87daSbmercer return 0;
91c03f87daSbmercer
92c03f87daSbmercer tree.header = (struct fdt_head *)fdt;
93c03f87daSbmercer tree.tree = (char *)fdt + betoh32(tree.header->fh_struct_off);
94c03f87daSbmercer tree.strings = (char *)fdt + betoh32(tree.header->fh_strings_off);
95c03f87daSbmercer tree.memory = (char *)fdt + betoh32(tree.header->fh_reserve_off);
961a409cc7Skettenis tree.end = (char *)fdt + betoh32(tree.header->fh_size);
97c03f87daSbmercer tree.version = version;
98c03f87daSbmercer tree.strings_size = betoh32(tree.header->fh_strings_size);
991a409cc7Skettenis if (tree.version >= 17)
1001a409cc7Skettenis tree.struct_size = betoh32(tree.header->fh_struct_size);
101c03f87daSbmercer tree_inited = 1;
102c03f87daSbmercer
103c03f87daSbmercer return version;
104c03f87daSbmercer }
105c03f87daSbmercer
1061a409cc7Skettenis void
fdt_finalize(void)1071a409cc7Skettenis fdt_finalize(void)
1081a409cc7Skettenis {
1091a409cc7Skettenis char *start = (char *)tree.header;
1101a409cc7Skettenis
1111a409cc7Skettenis tree.header->fh_size = htobe32(tree.end - start);
1121a409cc7Skettenis tree.header->fh_struct_off = htobe32(tree.tree - start);
1131a409cc7Skettenis tree.header->fh_strings_off = htobe32(tree.strings - start);
1141a409cc7Skettenis tree.header->fh_reserve_off = htobe32(tree.memory - start);
1151a409cc7Skettenis tree.header->fh_strings_size = htobe32(tree.strings_size);
1161a409cc7Skettenis if (tree.version >= 17)
1171a409cc7Skettenis tree.header->fh_struct_size = htobe32(tree.struct_size);
1181a409cc7Skettenis }
1191a409cc7Skettenis
120c03f87daSbmercer /*
121401e79efSpatrick * Return the size of the FDT.
122401e79efSpatrick */
123401e79efSpatrick size_t
fdt_get_size(void * fdt)124401e79efSpatrick fdt_get_size(void *fdt)
125401e79efSpatrick {
126401e79efSpatrick if (!fdt)
127401e79efSpatrick return 0;
128401e79efSpatrick
129401e79efSpatrick if (!fdt_check_head(fdt))
130401e79efSpatrick return 0;
131401e79efSpatrick
132401e79efSpatrick return betoh32(((struct fdt_head *)fdt)->fh_size);
133401e79efSpatrick }
134401e79efSpatrick
135401e79efSpatrick /*
136c03f87daSbmercer * Retrieve string pointer from strings table.
137c03f87daSbmercer */
138c03f87daSbmercer char *
fdt_get_str(u_int32_t num)139c03f87daSbmercer fdt_get_str(u_int32_t num)
140c03f87daSbmercer {
141c03f87daSbmercer if (num > tree.strings_size)
142c03f87daSbmercer return NULL;
143c03f87daSbmercer return (tree.strings) ? (tree.strings + num) : NULL;
144c03f87daSbmercer }
145c03f87daSbmercer
1461a409cc7Skettenis int
fdt_add_str(char * name)1471a409cc7Skettenis fdt_add_str(char *name)
1481a409cc7Skettenis {
1491a409cc7Skettenis size_t len = roundup(strlen(name) + 1, sizeof(uint32_t));
1501a409cc7Skettenis char *end = tree.strings + tree.strings_size;
1511a409cc7Skettenis
1521a409cc7Skettenis memmove(end + len, end, tree.end - end);
1531a409cc7Skettenis tree.strings_size += len;
1541a409cc7Skettenis if (tree.tree > tree.strings)
1551a409cc7Skettenis tree.tree += len;
1561a409cc7Skettenis if (tree.memory > tree.strings)
1571a409cc7Skettenis tree.memory += len;
1581a409cc7Skettenis tree.end += len;
1591a409cc7Skettenis memset(end, 0, len);
1601a409cc7Skettenis memcpy(end, name, strlen(name));
1611a409cc7Skettenis
1621a409cc7Skettenis return (end - tree.strings);
1631a409cc7Skettenis }
1641a409cc7Skettenis
165c03f87daSbmercer /*
166c03f87daSbmercer * Utility functions for skipping parts of tree.
167c03f87daSbmercer */
168808ecccaSvisa
169808ecccaSvisa void *
skip_nops(u_int32_t * ptr)170808ecccaSvisa skip_nops(u_int32_t *ptr)
171808ecccaSvisa {
172808ecccaSvisa while (betoh32(*ptr) == FDT_NOP)
173808ecccaSvisa ptr++;
174808ecccaSvisa
175808ecccaSvisa return ptr;
176808ecccaSvisa }
177808ecccaSvisa
178c03f87daSbmercer void *
skip_property(u_int32_t * ptr)179c03f87daSbmercer skip_property(u_int32_t *ptr)
180c03f87daSbmercer {
181c03f87daSbmercer u_int32_t size;
182c03f87daSbmercer
183c03f87daSbmercer size = betoh32(*(ptr + 1));
184c03f87daSbmercer /* move forward by magic + size + nameid + rounded up property size */
185c03f87daSbmercer ptr += 3 + roundup(size, sizeof(u_int32_t)) / sizeof(u_int32_t);
186c03f87daSbmercer
187808ecccaSvisa return skip_nops(ptr);
188c03f87daSbmercer }
189c03f87daSbmercer
190c03f87daSbmercer void *
skip_props(u_int32_t * ptr)191c03f87daSbmercer skip_props(u_int32_t *ptr)
192c03f87daSbmercer {
193c03f87daSbmercer while (betoh32(*ptr) == FDT_PROPERTY) {
194c03f87daSbmercer ptr = skip_property(ptr);
195c03f87daSbmercer }
196c03f87daSbmercer return ptr;
197c03f87daSbmercer }
198c03f87daSbmercer
199c03f87daSbmercer void *
skip_node_name(u_int32_t * ptr)200c03f87daSbmercer skip_node_name(u_int32_t *ptr)
201c03f87daSbmercer {
202c03f87daSbmercer /* skip name, aligned to 4 bytes, this is NULL term., so must add 1 */
203808ecccaSvisa ptr += roundup(strlen((char *)ptr) + 1,
204c03f87daSbmercer sizeof(u_int32_t)) / sizeof(u_int32_t);
205808ecccaSvisa
206808ecccaSvisa return skip_nops(ptr);
207c03f87daSbmercer }
208c03f87daSbmercer
209c03f87daSbmercer /*
210c03f87daSbmercer * Retrieves node property, the returned pointer is inside the fdt tree,
211c03f87daSbmercer * so we should not modify content pointed by it directly.
212c03f87daSbmercer */
213c03f87daSbmercer int
fdt_node_property(void * node,char * name,char ** out)214c03f87daSbmercer fdt_node_property(void *node, char *name, char **out)
215c03f87daSbmercer {
216c03f87daSbmercer u_int32_t *ptr;
217c03f87daSbmercer u_int32_t nameid;
218c03f87daSbmercer char *tmp;
219c03f87daSbmercer
220c03f87daSbmercer if (!tree_inited)
22195ccb12bSkettenis return -1;
222c03f87daSbmercer
223c03f87daSbmercer ptr = (u_int32_t *)node;
224c03f87daSbmercer
225c03f87daSbmercer if (betoh32(*ptr) != FDT_NODE_BEGIN)
22695ccb12bSkettenis return -1;
227c03f87daSbmercer
228c03f87daSbmercer ptr = skip_node_name(ptr + 1);
229c03f87daSbmercer
230c03f87daSbmercer while (betoh32(*ptr) == FDT_PROPERTY) {
231c03f87daSbmercer nameid = betoh32(*(ptr + 2)); /* id of name in strings table */
232c03f87daSbmercer tmp = fdt_get_str(nameid);
233c03f87daSbmercer if (!strcmp(name, tmp)) {
234c03f87daSbmercer *out = (char *)(ptr + 3); /* beginning of the value */
235c03f87daSbmercer return betoh32(*(ptr + 1)); /* size of value */
236c03f87daSbmercer }
237c03f87daSbmercer ptr = skip_property(ptr);
238c03f87daSbmercer }
23995ccb12bSkettenis return -1;
240c03f87daSbmercer }
241c03f87daSbmercer
2421a409cc7Skettenis int
fdt_node_set_property(void * node,char * name,void * data,int len)2431a409cc7Skettenis fdt_node_set_property(void *node, char *name, void *data, int len)
2441a409cc7Skettenis {
2451a409cc7Skettenis uint32_t *ptr, *next;
2461a409cc7Skettenis uint32_t nameid;
2471a409cc7Skettenis uint32_t curlen;
2481a409cc7Skettenis size_t delta;
2491a409cc7Skettenis char *tmp;
2501a409cc7Skettenis
2511a409cc7Skettenis if (!tree_inited)
2521a409cc7Skettenis return 0;
2531a409cc7Skettenis
2541a409cc7Skettenis ptr = (uint32_t *)node;
2551a409cc7Skettenis
2561a409cc7Skettenis if (betoh32(*ptr) != FDT_NODE_BEGIN)
2571a409cc7Skettenis return 0;
2581a409cc7Skettenis
2591a409cc7Skettenis ptr = skip_node_name(ptr + 1);
2601a409cc7Skettenis
2611a409cc7Skettenis while (betoh32(*ptr) == FDT_PROPERTY) {
2621a409cc7Skettenis nameid = betoh32(*(ptr + 2)); /* id of name in strings table */
2631a409cc7Skettenis tmp = fdt_get_str(nameid);
2641a409cc7Skettenis next = skip_property(ptr);
2651a409cc7Skettenis if (!strcmp(name, tmp)) {
2661a409cc7Skettenis curlen = betoh32(*(ptr + 1));
2671a409cc7Skettenis delta = roundup(len, sizeof(uint32_t)) -
2681a409cc7Skettenis roundup(curlen, sizeof(uint32_t));
2691a409cc7Skettenis memmove((char *)next + delta, next,
2701a409cc7Skettenis tree.end - (char *)next);
2711a409cc7Skettenis tree.struct_size += delta;
2721a409cc7Skettenis if (tree.strings > tree.tree)
2731a409cc7Skettenis tree.strings += delta;
2741a409cc7Skettenis if (tree.memory > tree.tree)
2751a409cc7Skettenis tree.memory += delta;
2761a409cc7Skettenis tree.end += delta;
2771a409cc7Skettenis *(ptr + 1) = htobe32(len);
2781a409cc7Skettenis memcpy(ptr + 3, data, len);
2791a409cc7Skettenis return 1;
2801a409cc7Skettenis }
2811a409cc7Skettenis ptr = next;
2821a409cc7Skettenis }
2831a409cc7Skettenis return 0;
2841a409cc7Skettenis }
2851a409cc7Skettenis
2861a409cc7Skettenis int
fdt_node_add_property(void * node,char * name,void * data,int len)2871a409cc7Skettenis fdt_node_add_property(void *node, char *name, void *data, int len)
2881a409cc7Skettenis {
2891a409cc7Skettenis char *dummy;
2901a409cc7Skettenis
2911a409cc7Skettenis if (!tree_inited)
2921a409cc7Skettenis return 0;
2931a409cc7Skettenis
2941a409cc7Skettenis if (fdt_node_property(node, name, &dummy) == -1) {
2951a409cc7Skettenis uint32_t *ptr = (uint32_t *)node;
2961a409cc7Skettenis
2971a409cc7Skettenis if (betoh32(*ptr) != FDT_NODE_BEGIN)
2981a409cc7Skettenis return 0;
2991a409cc7Skettenis
3001a409cc7Skettenis ptr = skip_node_name(ptr + 1);
3011a409cc7Skettenis
3021a409cc7Skettenis memmove(ptr + 3, ptr, tree.end - (char *)ptr);
3031a409cc7Skettenis tree.struct_size += 3 * sizeof(uint32_t);
3041a409cc7Skettenis if (tree.strings > tree.tree)
3051a409cc7Skettenis tree.strings += 3 * sizeof(uint32_t);
3061a409cc7Skettenis if (tree.memory > tree.tree)
3071a409cc7Skettenis tree.memory += 3 * sizeof(uint32_t);
3081a409cc7Skettenis tree.end += 3 * sizeof(uint32_t);
3091a409cc7Skettenis *ptr++ = htobe32(FDT_PROPERTY);
3101a409cc7Skettenis *ptr++ = htobe32(0);
3111a409cc7Skettenis *ptr++ = htobe32(fdt_add_str(name));
3121a409cc7Skettenis }
3131a409cc7Skettenis
3141a409cc7Skettenis return fdt_node_set_property(node, name, data, len);
3151a409cc7Skettenis }
3161a409cc7Skettenis
317c03f87daSbmercer /*
318dd86c6a0Spatrick * Retrieves next node, skipping all the children nodes of the pointed node,
319dd86c6a0Spatrick * returns pointer to next node, no matter if it exists or not.
320dd86c6a0Spatrick */
321dd86c6a0Spatrick void *
skip_node(void * node)322dd86c6a0Spatrick skip_node(void *node)
323dd86c6a0Spatrick {
324dd86c6a0Spatrick u_int32_t *ptr = node;
325dd86c6a0Spatrick
326dd86c6a0Spatrick ptr++;
327dd86c6a0Spatrick
328dd86c6a0Spatrick ptr = skip_node_name(ptr);
329dd86c6a0Spatrick ptr = skip_props(ptr);
330dd86c6a0Spatrick
331dd86c6a0Spatrick /* skip children */
332dd86c6a0Spatrick while (betoh32(*ptr) == FDT_NODE_BEGIN)
333dd86c6a0Spatrick ptr = skip_node(ptr);
334dd86c6a0Spatrick
335808ecccaSvisa return skip_nops(ptr + 1);
336dd86c6a0Spatrick }
337dd86c6a0Spatrick
338dd86c6a0Spatrick /*
339dd86c6a0Spatrick * Retrieves next node, skipping all the children nodes of the pointed node,
340dd86c6a0Spatrick * returns pointer to next node if exists, otherwise returns NULL.
341dd86c6a0Spatrick * If passed 0 will return first node of the tree (root).
342c03f87daSbmercer */
343c03f87daSbmercer void *
fdt_next_node(void * node)344c03f87daSbmercer fdt_next_node(void *node)
345c03f87daSbmercer {
346c03f87daSbmercer u_int32_t *ptr;
347c03f87daSbmercer
348c03f87daSbmercer if (!tree_inited)
349c03f87daSbmercer return NULL;
350c03f87daSbmercer
351c03f87daSbmercer ptr = node;
352c03f87daSbmercer
353dd86c6a0Spatrick if (node == NULL) {
3541a409cc7Skettenis ptr = skip_nops((uint32_t *)tree.tree);
355c03f87daSbmercer return (betoh32(*ptr) == FDT_NODE_BEGIN) ? ptr : NULL;
356c03f87daSbmercer }
357c03f87daSbmercer
358c03f87daSbmercer if (betoh32(*ptr) != FDT_NODE_BEGIN)
359c03f87daSbmercer return NULL;
360c03f87daSbmercer
361c03f87daSbmercer ptr++;
362c03f87daSbmercer
363c03f87daSbmercer ptr = skip_node_name(ptr);
364c03f87daSbmercer ptr = skip_props(ptr);
365c03f87daSbmercer
366c03f87daSbmercer /* skip children */
367c03f87daSbmercer while (betoh32(*ptr) == FDT_NODE_BEGIN)
368dd86c6a0Spatrick ptr = skip_node(ptr);
369c03f87daSbmercer
370dd86c6a0Spatrick if (betoh32(*ptr) != FDT_NODE_END)
371dd86c6a0Spatrick return NULL;
372dd86c6a0Spatrick
373808ecccaSvisa ptr = skip_nops(ptr + 1);
374808ecccaSvisa
375808ecccaSvisa if (betoh32(*ptr) != FDT_NODE_BEGIN)
376dd86c6a0Spatrick return NULL;
377dd86c6a0Spatrick
378808ecccaSvisa return ptr;
379c03f87daSbmercer }
380c03f87daSbmercer
381d571cddaSkettenis int
fdt_next_property(void * node,char * name,char ** nextname)382d571cddaSkettenis fdt_next_property(void *node, char *name, char **nextname)
383d571cddaSkettenis {
384d571cddaSkettenis u_int32_t *ptr;
385d571cddaSkettenis u_int32_t nameid;
386d571cddaSkettenis
387d571cddaSkettenis if (!tree_inited)
388d571cddaSkettenis return 0;
389d571cddaSkettenis
390d571cddaSkettenis ptr = (u_int32_t *)node;
391d571cddaSkettenis
392d571cddaSkettenis if (betoh32(*ptr) != FDT_NODE_BEGIN)
393d571cddaSkettenis return 0;
394d571cddaSkettenis
395d571cddaSkettenis ptr = skip_node_name(ptr + 1);
396d571cddaSkettenis
397d571cddaSkettenis while (betoh32(*ptr) == FDT_PROPERTY) {
398d571cddaSkettenis nameid = betoh32(*(ptr + 2)); /* id of name in strings table */
399d571cddaSkettenis if (strcmp(name, "") == 0) {
400d571cddaSkettenis *nextname = fdt_get_str(nameid);
401d571cddaSkettenis return 1;
402d571cddaSkettenis }
403d571cddaSkettenis if (strcmp(name, fdt_get_str(nameid)) == 0) {
404d571cddaSkettenis ptr = skip_property(ptr);
405d571cddaSkettenis if (betoh32(*ptr) != FDT_PROPERTY)
406d571cddaSkettenis break;
407d571cddaSkettenis nameid = betoh32(*(ptr + 2));
408d571cddaSkettenis *nextname = fdt_get_str(nameid);
409d571cddaSkettenis return 1;
410d571cddaSkettenis }
411d571cddaSkettenis ptr = skip_property(ptr);
412d571cddaSkettenis }
413d571cddaSkettenis *nextname = "";
414d571cddaSkettenis return 1;
415d571cddaSkettenis }
416d571cddaSkettenis
417c03f87daSbmercer /*
418401e79efSpatrick * Retrieves node property as integers and puts them in the given
419401e79efSpatrick * integer array.
420401e79efSpatrick */
421401e79efSpatrick int
fdt_node_property_ints(void * node,char * name,int * out,int outlen)422401e79efSpatrick fdt_node_property_ints(void *node, char *name, int *out, int outlen)
423401e79efSpatrick {
424401e79efSpatrick int *data;
425401e79efSpatrick int i, inlen;
426401e79efSpatrick
427401e79efSpatrick inlen = fdt_node_property(node, name, (char **)&data) / sizeof(int);
428401e79efSpatrick if (inlen <= 0)
429401e79efSpatrick return -1;
430401e79efSpatrick
431401e79efSpatrick for (i = 0; i < inlen && i < outlen; i++)
432401e79efSpatrick out[i] = betoh32(data[i]);
433401e79efSpatrick
434401e79efSpatrick return i;
435401e79efSpatrick }
436401e79efSpatrick
437401e79efSpatrick /*
438401e79efSpatrick * Retrieves node property as an integer.
439401e79efSpatrick */
440401e79efSpatrick int
fdt_node_property_int(void * node,char * name,int * out)441401e79efSpatrick fdt_node_property_int(void *node, char *name, int *out)
442401e79efSpatrick {
443401e79efSpatrick return fdt_node_property_ints(node, name, out, 1);
444401e79efSpatrick }
445401e79efSpatrick
446401e79efSpatrick /*
447c03f87daSbmercer * Retrieves next node, skipping all the children nodes of the pointed node
448c03f87daSbmercer */
449c03f87daSbmercer void *
fdt_child_node(void * node)450c03f87daSbmercer fdt_child_node(void *node)
451c03f87daSbmercer {
452c03f87daSbmercer u_int32_t *ptr;
453c03f87daSbmercer
454c03f87daSbmercer if (!tree_inited)
455c03f87daSbmercer return NULL;
456c03f87daSbmercer
457c03f87daSbmercer ptr = node;
458c03f87daSbmercer
459c03f87daSbmercer if (betoh32(*ptr) != FDT_NODE_BEGIN)
460c03f87daSbmercer return NULL;
461c03f87daSbmercer
462c03f87daSbmercer ptr++;
463c03f87daSbmercer
464c03f87daSbmercer ptr = skip_node_name(ptr);
465c03f87daSbmercer ptr = skip_props(ptr);
466c03f87daSbmercer /* check if there is a child node */
467c03f87daSbmercer return (betoh32(*ptr) == FDT_NODE_BEGIN) ? (ptr) : NULL;
468c03f87daSbmercer }
469c03f87daSbmercer
470c03f87daSbmercer /*
471c03f87daSbmercer * Retrieves node name.
472c03f87daSbmercer */
473c03f87daSbmercer char *
fdt_node_name(void * node)474c03f87daSbmercer fdt_node_name(void *node)
475c03f87daSbmercer {
476c03f87daSbmercer u_int32_t *ptr;
477c03f87daSbmercer
478c03f87daSbmercer if (!tree_inited)
479c03f87daSbmercer return NULL;
480c03f87daSbmercer
481c03f87daSbmercer ptr = node;
482c03f87daSbmercer
483c03f87daSbmercer if (betoh32(*ptr) != FDT_NODE_BEGIN)
484c03f87daSbmercer return NULL;
485c03f87daSbmercer
486c03f87daSbmercer return (char *)(ptr + 1);
487c03f87daSbmercer }
488c03f87daSbmercer
489c03f87daSbmercer void *
fdt_find_node(char * name)490c03f87daSbmercer fdt_find_node(char *name)
491c03f87daSbmercer {
492c03f87daSbmercer void *node = fdt_next_node(0);
493c03f87daSbmercer const char *p = name;
494c03f87daSbmercer
495c03f87daSbmercer if (!tree_inited)
496c03f87daSbmercer return NULL;
497c03f87daSbmercer
498c03f87daSbmercer if (*p != '/')
499c03f87daSbmercer return NULL;
500c03f87daSbmercer
501c03f87daSbmercer while (*p) {
502c03f87daSbmercer void *child;
503c03f87daSbmercer const char *q;
504*3fe5dae2Skettenis const char *s;
505c03f87daSbmercer
506c03f87daSbmercer while (*p == '/')
507c03f87daSbmercer p++;
508c03f87daSbmercer if (*p == 0)
509c03f87daSbmercer return node;
510c03f87daSbmercer q = strchr(p, '/');
511c03f87daSbmercer if (q == NULL)
512c03f87daSbmercer q = p + strlen(p);
513c03f87daSbmercer
514*3fe5dae2Skettenis /* Check for a complete match. */
515c03f87daSbmercer for (child = fdt_child_node(node); child;
516c03f87daSbmercer child = fdt_next_node(child)) {
517*3fe5dae2Skettenis s = fdt_node_name(child);
518*3fe5dae2Skettenis if (strncmp(p, s, q - p) == 0 && s[q - p] == '\0')
519c03f87daSbmercer break;
520c03f87daSbmercer }
521*3fe5dae2Skettenis if (child) {
522*3fe5dae2Skettenis node = child;
523*3fe5dae2Skettenis p = q;
524*3fe5dae2Skettenis continue;
525c03f87daSbmercer }
526c03f87daSbmercer
527*3fe5dae2Skettenis /* Check for a match without the unit name. */
528*3fe5dae2Skettenis for (child = fdt_child_node(node); child;
529*3fe5dae2Skettenis child = fdt_next_node(child)) {
530*3fe5dae2Skettenis s = fdt_node_name(child);
531*3fe5dae2Skettenis if (strncmp(p, s, q - p) == 0 && s[q - p] == '@')
532*3fe5dae2Skettenis break;
533*3fe5dae2Skettenis }
534*3fe5dae2Skettenis if (child) {
535*3fe5dae2Skettenis node = child;
536c03f87daSbmercer p = q;
537*3fe5dae2Skettenis continue;
538*3fe5dae2Skettenis }
539*3fe5dae2Skettenis
540*3fe5dae2Skettenis return NULL; /* No match found. */
541c03f87daSbmercer }
542c03f87daSbmercer
543c03f87daSbmercer return node;
544c03f87daSbmercer }
545c03f87daSbmercer
546c03f87daSbmercer void *
fdt_parent_node_recurse(void * pnode,void * child)547c03f87daSbmercer fdt_parent_node_recurse(void *pnode, void *child)
548c03f87daSbmercer {
549c03f87daSbmercer void *node = fdt_child_node(pnode);
550c03f87daSbmercer void *tmp;
551c03f87daSbmercer
552c03f87daSbmercer while (node && (node != child)) {
553c03f87daSbmercer if ((tmp = fdt_parent_node_recurse(node, child)))
554c03f87daSbmercer return tmp;
555c03f87daSbmercer node = fdt_next_node(node);
556c03f87daSbmercer }
557c03f87daSbmercer return (node) ? pnode : NULL;
558c03f87daSbmercer }
559c03f87daSbmercer
560c03f87daSbmercer void *
fdt_parent_node(void * node)561c03f87daSbmercer fdt_parent_node(void *node)
562c03f87daSbmercer {
563c03f87daSbmercer void *pnode = fdt_next_node(0);
564c03f87daSbmercer
565c03f87daSbmercer if (!tree_inited)
566c03f87daSbmercer return NULL;
567c03f87daSbmercer
568b9dd9f55Smpi if (node == pnode)
569b9dd9f55Smpi return NULL;
570b9dd9f55Smpi
571c03f87daSbmercer return fdt_parent_node_recurse(pnode, node);
572c03f87daSbmercer }
573c03f87daSbmercer
574d0cd2b3aSkettenis void *
fdt_find_phandle_recurse(void * node,uint32_t phandle)575d0cd2b3aSkettenis fdt_find_phandle_recurse(void *node, uint32_t phandle)
576d0cd2b3aSkettenis {
577d0cd2b3aSkettenis void *child;
578d0cd2b3aSkettenis char *data;
579d0cd2b3aSkettenis void *tmp;
580d0cd2b3aSkettenis int len;
581d0cd2b3aSkettenis
582d0cd2b3aSkettenis len = fdt_node_property(node, "phandle", &data);
583d0cd2b3aSkettenis if (len < 0)
584d0cd2b3aSkettenis len = fdt_node_property(node, "linux,phandle", &data);
585d0cd2b3aSkettenis
586d0cd2b3aSkettenis if (len == sizeof(uint32_t) && bemtoh32(data) == phandle)
587d0cd2b3aSkettenis return node;
588d0cd2b3aSkettenis
589d0cd2b3aSkettenis for (child = fdt_child_node(node); child; child = fdt_next_node(child))
590d0cd2b3aSkettenis if ((tmp = fdt_find_phandle_recurse(child, phandle)))
591d0cd2b3aSkettenis return tmp;
592d0cd2b3aSkettenis
593d0cd2b3aSkettenis return NULL;
594d0cd2b3aSkettenis }
595d0cd2b3aSkettenis
596d0cd2b3aSkettenis void *
fdt_find_phandle(uint32_t phandle)597d0cd2b3aSkettenis fdt_find_phandle(uint32_t phandle)
598d0cd2b3aSkettenis {
599d0cd2b3aSkettenis return fdt_find_phandle_recurse(fdt_next_node(0), phandle);
600d0cd2b3aSkettenis }
601d0cd2b3aSkettenis
602598f9116Skettenis void
fdt_get_cells(void * node,int * ac,int * sc)603598f9116Skettenis fdt_get_cells(void *node, int *ac, int *sc)
604598f9116Skettenis {
605598f9116Skettenis void *parent;
606598f9116Skettenis
607598f9116Skettenis parent = fdt_parent_node(node);
608598f9116Skettenis if (parent == NULL)
609598f9116Skettenis *ac = *sc = 1;
610598f9116Skettenis else
611598f9116Skettenis fdt_get_cells(parent, ac, sc);
612598f9116Skettenis
613598f9116Skettenis fdt_node_property_int(node, "#address-cells", ac);
614598f9116Skettenis fdt_node_property_int(node, "#size-cells", sc);
615598f9116Skettenis }
616598f9116Skettenis
617401e79efSpatrick /*
6187858eedaSpatrick * Translate memory address depending on parent's range.
6197858eedaSpatrick *
6207858eedaSpatrick * Ranges are a way of mapping one address to another. This ranges attribute
6217858eedaSpatrick * is set on a node's parent. This means if a node does not have a parent,
6227858eedaSpatrick * there's nothing to translate. If it does have a parent and the parent does
6237858eedaSpatrick * not have a ranges attribute, there's nothing to translate either.
6247858eedaSpatrick *
6257858eedaSpatrick * If the parent has a ranges attribute and the attribute is not empty, the
6267858eedaSpatrick * node's memory address has to be in one of the given ranges. This range is
6277858eedaSpatrick * then used to translate the memory address.
6287858eedaSpatrick *
6297858eedaSpatrick * If the parent has a ranges attribute, but the attribute is empty, there's
6307858eedaSpatrick * nothing to translate. But it's not a translation barrier. It can be treated
6317858eedaSpatrick * as a simple 1:1 mapping.
6327858eedaSpatrick *
6337858eedaSpatrick * Translation does not end here. We need to check if the parent's parent also
6347858eedaSpatrick * has a ranges attribute and ask the same questions again.
6357858eedaSpatrick */
6367858eedaSpatrick int
fdt_translate_reg(void * node,struct fdt_reg * reg)637b7952965Spatrick fdt_translate_reg(void *node, struct fdt_reg *reg)
6387858eedaSpatrick {
6397858eedaSpatrick void *parent;
640598f9116Skettenis int pac, psc, ac, sc, rlen, rone, *range;
6417858eedaSpatrick uint64_t from, to, size;
6427858eedaSpatrick
6437858eedaSpatrick /* No parent, no translation. */
6447858eedaSpatrick parent = fdt_parent_node(node);
6457858eedaSpatrick if (parent == NULL)
6467858eedaSpatrick return 0;
6477858eedaSpatrick
6487858eedaSpatrick /* Extract ranges property from node. */
6497858eedaSpatrick rlen = fdt_node_property(node, "ranges", (char **)&range) / sizeof(int);
6507858eedaSpatrick
6517858eedaSpatrick /* No ranges means translation barrier. Translation stops here. */
6527858eedaSpatrick if (range == NULL)
6537858eedaSpatrick return 0;
6547858eedaSpatrick
6557858eedaSpatrick /* Empty ranges means 1:1 mapping. Continue translation on parent. */
6567858eedaSpatrick if (rlen <= 0)
657b7952965Spatrick return fdt_translate_reg(parent, reg);
6587858eedaSpatrick
659598f9116Skettenis /*
660598f9116Skettenis * Get parent address/size width. We only support 32-bit (1)
661598f9116Skettenis * and 64-bit (2) wide addresses and sizes here.
662598f9116Skettenis */
663598f9116Skettenis fdt_get_cells(parent, &pac, &psc);
664598f9116Skettenis if (pac <= 0 || pac > 2 || psc <= 0 || psc > 2)
6657858eedaSpatrick return EINVAL;
6667858eedaSpatrick
667598f9116Skettenis /*
668598f9116Skettenis * Get our own address/size width. Again, we only support
669598f9116Skettenis * 32-bit (1) and 64-bit (2) wide addresses and sizes here.
670598f9116Skettenis */
671598f9116Skettenis fdt_get_cells(node, &ac, &sc);
672598f9116Skettenis if (ac <= 0 || ac > 2 || sc <= 0 || sc > 2)
6737858eedaSpatrick return EINVAL;
6747858eedaSpatrick
6757858eedaSpatrick /* Must have at least one range. */
6767858eedaSpatrick rone = pac + ac + sc;
6777858eedaSpatrick if (rlen < rone)
6787858eedaSpatrick return ESRCH;
6797858eedaSpatrick
6807858eedaSpatrick /* For each range. */
6817858eedaSpatrick for (; rlen >= rone; rlen -= rone, range += rone) {
6827858eedaSpatrick /* Extract from and size, so we can see if we fit. */
6837858eedaSpatrick from = betoh32(range[0]);
6847858eedaSpatrick if (ac == 2)
6857858eedaSpatrick from = (from << 32) + betoh32(range[1]);
6867858eedaSpatrick size = betoh32(range[ac + pac]);
6877858eedaSpatrick if (sc == 2)
6887858eedaSpatrick size = (size << 32) + betoh32(range[ac + pac + 1]);
6897858eedaSpatrick
6907858eedaSpatrick /* Try next, if we're not in the range. */
691b7952965Spatrick if (reg->addr < from || (reg->addr + reg->size) > (from + size))
6927858eedaSpatrick continue;
6937858eedaSpatrick
6947858eedaSpatrick /* All good, extract to address and translate. */
6957858eedaSpatrick to = betoh32(range[ac]);
6967858eedaSpatrick if (pac == 2)
6977858eedaSpatrick to = (to << 32) + betoh32(range[ac + 1]);
6987858eedaSpatrick
699b7952965Spatrick reg->addr -= from;
700b7952965Spatrick reg->addr += to;
701b7952965Spatrick return fdt_translate_reg(parent, reg);
7027858eedaSpatrick }
7037858eedaSpatrick
7047858eedaSpatrick /* To be successful, we must have returned in the for-loop. */
7057858eedaSpatrick return ESRCH;
7067858eedaSpatrick }
7077858eedaSpatrick
7087858eedaSpatrick /*
709401e79efSpatrick * Parse the memory address and size of a node.
710401e79efSpatrick */
711401e79efSpatrick int
fdt_get_reg(void * node,int idx,struct fdt_reg * reg)712b7952965Spatrick fdt_get_reg(void *node, int idx, struct fdt_reg *reg)
713401e79efSpatrick {
714401e79efSpatrick void *parent;
715598f9116Skettenis int ac, sc, off, *in, inlen;
716401e79efSpatrick
717b7952965Spatrick if (node == NULL || reg == NULL)
718ed972280Spatrick return EINVAL;
719401e79efSpatrick
720401e79efSpatrick parent = fdt_parent_node(node);
721401e79efSpatrick if (parent == NULL)
722ed972280Spatrick return EINVAL;
723401e79efSpatrick
724598f9116Skettenis /*
725598f9116Skettenis * Get parent address/size width. We only support 32-bit (1)
726598f9116Skettenis * and 64-bit (2) wide addresses and sizes here.
727598f9116Skettenis */
728598f9116Skettenis fdt_get_cells(parent, &ac, &sc);
729598f9116Skettenis if (ac <= 0 || ac > 2 || sc <= 0 || sc > 2)
730ed972280Spatrick return EINVAL;
731401e79efSpatrick
732401e79efSpatrick inlen = fdt_node_property(node, "reg", (char **)&in) / sizeof(int);
733401e79efSpatrick if (inlen < ((idx + 1) * (ac + sc)))
734ed972280Spatrick return EINVAL;
735401e79efSpatrick
736401e79efSpatrick off = idx * (ac + sc);
737401e79efSpatrick
738b7952965Spatrick reg->addr = betoh32(in[off]);
739401e79efSpatrick if (ac == 2)
740b7952965Spatrick reg->addr = (reg->addr << 32) + betoh32(in[off + 1]);
741401e79efSpatrick
742b7952965Spatrick reg->size = betoh32(in[off + ac]);
743401e79efSpatrick if (sc == 2)
744b7952965Spatrick reg->size = (reg->size << 32) + betoh32(in[off + ac + 1]);
745401e79efSpatrick
746b7952965Spatrick return fdt_translate_reg(parent, reg);
747401e79efSpatrick }
748401e79efSpatrick
749557e6971Sjsg int
fdt_is_compatible(void * node,const char * name)750557e6971Sjsg fdt_is_compatible(void *node, const char *name)
751557e6971Sjsg {
752557e6971Sjsg char *data;
753557e6971Sjsg int len;
754557e6971Sjsg
755557e6971Sjsg len = fdt_node_property(node, "compatible", &data);
756557e6971Sjsg while (len > 0) {
757557e6971Sjsg if (strcmp(data, name) == 0)
758557e6971Sjsg return 1;
759557e6971Sjsg len -= strlen(data) + 1;
760557e6971Sjsg data += strlen(data) + 1;
761557e6971Sjsg }
762557e6971Sjsg
763557e6971Sjsg return 0;
764557e6971Sjsg }
765557e6971Sjsg
766c03f87daSbmercer #ifdef DEBUG
767c03f87daSbmercer /*
7684b1a56afSjsg * Debug methods for printing whole tree, particular nodes and properties
769c03f87daSbmercer */
770c03f87daSbmercer void *
fdt_print_property(void * node,int level)771c03f87daSbmercer fdt_print_property(void *node, int level)
772c03f87daSbmercer {
773c03f87daSbmercer u_int32_t *ptr;
774c03f87daSbmercer char *tmp, *value;
775c03f87daSbmercer int cnt;
776c03f87daSbmercer u_int32_t nameid, size;
777c03f87daSbmercer
778c03f87daSbmercer ptr = (u_int32_t *)node;
779c03f87daSbmercer
780c03f87daSbmercer if (!tree_inited)
781c03f87daSbmercer return NULL;
782c03f87daSbmercer
783c03f87daSbmercer if (betoh32(*ptr) != FDT_PROPERTY)
784c03f87daSbmercer return ptr; /* should never happen */
785c03f87daSbmercer
786c03f87daSbmercer /* extract property name_id and size */
787c03f87daSbmercer size = betoh32(*++ptr);
788c03f87daSbmercer nameid = betoh32(*++ptr);
789c03f87daSbmercer
790c03f87daSbmercer for (cnt = 0; cnt < level; cnt++)
791c03f87daSbmercer printf("\t");
792c03f87daSbmercer
793c03f87daSbmercer tmp = fdt_get_str(nameid);
794c03f87daSbmercer printf("\t%s : ", tmp ? tmp : "NO_NAME");
795c03f87daSbmercer
796c03f87daSbmercer ptr++;
797c03f87daSbmercer value = (char *)ptr;
798c03f87daSbmercer
799c03f87daSbmercer if (!strcmp(tmp, "device_type") || !strcmp(tmp, "compatible") ||
800c03f87daSbmercer !strcmp(tmp, "model") || !strcmp(tmp, "bootargs") ||
801c03f87daSbmercer !strcmp(tmp, "linux,stdout-path")) {
802c03f87daSbmercer printf("%s", value);
803c03f87daSbmercer } else if (!strcmp(tmp, "clock-frequency") ||
804c03f87daSbmercer !strcmp(tmp, "timebase-frequency")) {
805c03f87daSbmercer printf("%d", betoh32(*((unsigned int *)value)));
806c03f87daSbmercer } else {
807c03f87daSbmercer for (cnt = 0; cnt < size; cnt++) {
808c03f87daSbmercer if ((cnt % sizeof(u_int32_t)) == 0)
809c03f87daSbmercer printf(" ");
810c03f87daSbmercer printf("%02x", value[cnt]);
811c03f87daSbmercer }
812c03f87daSbmercer }
813c03f87daSbmercer ptr += roundup(size, sizeof(u_int32_t)) / sizeof(u_int32_t);
814c03f87daSbmercer printf("\n");
815c03f87daSbmercer
816c03f87daSbmercer return ptr;
817c03f87daSbmercer }
818c03f87daSbmercer
819c03f87daSbmercer void
fdt_print_node(void * node,int level)820c03f87daSbmercer fdt_print_node(void *node, int level)
821c03f87daSbmercer {
822c03f87daSbmercer u_int32_t *ptr;
823c03f87daSbmercer int cnt;
824c03f87daSbmercer
825c03f87daSbmercer ptr = (u_int32_t *)node;
826c03f87daSbmercer
827c03f87daSbmercer if (betoh32(*ptr) != FDT_NODE_BEGIN)
828c03f87daSbmercer return;
829c03f87daSbmercer
830c03f87daSbmercer ptr++;
831c03f87daSbmercer
832c03f87daSbmercer for (cnt = 0; cnt < level; cnt++)
833c03f87daSbmercer printf("\t");
834c03f87daSbmercer printf("%s :\n", fdt_node_name(node));
835c03f87daSbmercer ptr = skip_node_name(ptr);
836c03f87daSbmercer
837c03f87daSbmercer while (betoh32(*ptr) == FDT_PROPERTY)
838c03f87daSbmercer ptr = fdt_print_property(ptr, level);
839c03f87daSbmercer }
840c03f87daSbmercer
841c03f87daSbmercer void
fdt_print_node_recurse(void * node,int level)842c03f87daSbmercer fdt_print_node_recurse(void *node, int level)
843c03f87daSbmercer {
844c03f87daSbmercer void *child;
845c03f87daSbmercer
846c03f87daSbmercer fdt_print_node(node, level);
847c03f87daSbmercer for (child = fdt_child_node(node); child; child = fdt_next_node(child))
848c03f87daSbmercer fdt_print_node_recurse(child, level + 1);
849c03f87daSbmercer }
850c03f87daSbmercer
851c03f87daSbmercer void
fdt_print_tree(void)852c03f87daSbmercer fdt_print_tree(void)
853c03f87daSbmercer {
854c03f87daSbmercer fdt_print_node_recurse(fdt_next_node(0), 0);
855c03f87daSbmercer }
856c03f87daSbmercer #endif
857c03f87daSbmercer
858c03f87daSbmercer int
OF_peer(int handle)859c03f87daSbmercer OF_peer(int handle)
860c03f87daSbmercer {
861c03f87daSbmercer void *node = (char *)tree.header + handle;
862c03f87daSbmercer
863c03f87daSbmercer if (handle == 0)
864c03f87daSbmercer node = fdt_find_node("/");
865c03f87daSbmercer else
866c03f87daSbmercer node = fdt_next_node(node);
867c03f87daSbmercer return node ? ((char *)node - (char *)tree.header) : 0;
868c03f87daSbmercer }
869c03f87daSbmercer
870c03f87daSbmercer int
OF_child(int handle)871c03f87daSbmercer OF_child(int handle)
872c03f87daSbmercer {
873c03f87daSbmercer void *node = (char *)tree.header + handle;
874c03f87daSbmercer
875c03f87daSbmercer node = fdt_child_node(node);
876c03f87daSbmercer return node ? ((char *)node - (char *)tree.header) : 0;
877c03f87daSbmercer }
878c03f87daSbmercer
879c03f87daSbmercer int
OF_parent(int handle)880c03f87daSbmercer OF_parent(int handle)
881c03f87daSbmercer {
882c03f87daSbmercer void *node = (char *)tree.header + handle;
883c03f87daSbmercer
884c03f87daSbmercer node = fdt_parent_node(node);
885c03f87daSbmercer return node ? ((char *)node - (char *)tree.header) : 0;
886c03f87daSbmercer }
887c03f87daSbmercer
888c03f87daSbmercer int
OF_finddevice(char * name)889c03f87daSbmercer OF_finddevice(char *name)
890c03f87daSbmercer {
891c03f87daSbmercer void *node;
892c03f87daSbmercer
893c03f87daSbmercer node = fdt_find_node(name);
894c03f87daSbmercer return node ? ((char *)node - (char *)tree.header) : -1;
895c03f87daSbmercer }
896c03f87daSbmercer
897c03f87daSbmercer int
OF_getnodebyname(int handle,const char * name)898d571cddaSkettenis OF_getnodebyname(int handle, const char *name)
899d571cddaSkettenis {
900d571cddaSkettenis void *node = (char *)tree.header + handle;
901e807a99bSpatrick void *child;
902e807a99bSpatrick char *data;
903e807a99bSpatrick int len;
904d571cddaSkettenis
905d571cddaSkettenis if (handle == 0)
906d571cddaSkettenis node = fdt_find_node("/");
907d571cddaSkettenis
908e807a99bSpatrick for (child = fdt_child_node(node); child;
909e807a99bSpatrick child = fdt_next_node(child)) {
910e807a99bSpatrick if (strcmp(name, fdt_node_name(child)) == 0)
911d571cddaSkettenis break;
912d571cddaSkettenis }
913e807a99bSpatrick if (child)
914e807a99bSpatrick return (char *)child - (char *)tree.header;
915d571cddaSkettenis
916e807a99bSpatrick len = strlen(name);
917e807a99bSpatrick for (child = fdt_child_node(node); child;
918e807a99bSpatrick child = fdt_next_node(child)) {
919e807a99bSpatrick data = fdt_node_name(child);
920e807a99bSpatrick if (strncmp(name, data, len) == 0 &&
921e807a99bSpatrick strlen(data) > len && data[len] == '@')
922e807a99bSpatrick break;
923e807a99bSpatrick }
924e807a99bSpatrick if (child)
925e807a99bSpatrick return (char *)child - (char *)tree.header;
926e807a99bSpatrick
927e807a99bSpatrick return 0;
928d571cddaSkettenis }
929d571cddaSkettenis
930d571cddaSkettenis int
OF_getnodebyphandle(uint32_t phandle)931d0cd2b3aSkettenis OF_getnodebyphandle(uint32_t phandle)
932d0cd2b3aSkettenis {
933d0cd2b3aSkettenis void *node;
934d0cd2b3aSkettenis
935d0cd2b3aSkettenis node = fdt_find_phandle(phandle);
936d0cd2b3aSkettenis return node ? ((char *)node - (char *)tree.header) : 0;
937d0cd2b3aSkettenis }
938d0cd2b3aSkettenis
939d0cd2b3aSkettenis int
OF_getproplen(int handle,char * prop)940c03f87daSbmercer OF_getproplen(int handle, char *prop)
941c03f87daSbmercer {
942c03f87daSbmercer void *node = (char *)tree.header + handle;
943d571cddaSkettenis char *data, *name;
944d571cddaSkettenis int len;
945c03f87daSbmercer
946d571cddaSkettenis len = fdt_node_property(node, prop, &data);
947d571cddaSkettenis
948d571cddaSkettenis /*
949d571cddaSkettenis * The "name" property is optional since version 16 of the
950d571cddaSkettenis * flattened device tree specification, so we synthesize one
951d571cddaSkettenis * from the unit name of the node if it is missing.
952d571cddaSkettenis */
95395ccb12bSkettenis if (len < 0 && strcmp(prop, "name") == 0) {
954d571cddaSkettenis name = fdt_node_name(node);
955d571cddaSkettenis data = strchr(name, '@');
956d571cddaSkettenis if (data)
957d571cddaSkettenis len = data - name;
958d571cddaSkettenis else
959d571cddaSkettenis len = strlen(name);
960d571cddaSkettenis return len + 1;
961d571cddaSkettenis }
962d571cddaSkettenis
963d571cddaSkettenis return len;
964c03f87daSbmercer }
965c03f87daSbmercer
966c03f87daSbmercer int
OF_getprop(int handle,char * prop,void * buf,int buflen)967c03f87daSbmercer OF_getprop(int handle, char *prop, void *buf, int buflen)
968c03f87daSbmercer {
969c03f87daSbmercer void *node = (char *)tree.header + handle;
970c03f87daSbmercer char *data;
971c03f87daSbmercer int len;
972c03f87daSbmercer
973c03f87daSbmercer len = fdt_node_property(node, prop, &data);
974c03f87daSbmercer
975c03f87daSbmercer /*
976c03f87daSbmercer * The "name" property is optional since version 16 of the
977c03f87daSbmercer * flattened device tree specification, so we synthesize one
978c03f87daSbmercer * from the unit name of the node if it is missing.
979c03f87daSbmercer */
98095ccb12bSkettenis if (len < 0 && strcmp(prop, "name") == 0) {
981c03f87daSbmercer data = fdt_node_name(node);
982c03f87daSbmercer if (data) {
983c03f87daSbmercer len = strlcpy(buf, data, buflen);
984c03f87daSbmercer data = strchr(buf, '@');
98572a32a39Skettenis if (data) {
986c03f87daSbmercer *data = 0;
98772a32a39Skettenis len = data - (char *)buf;
98872a32a39Skettenis }
989c03f87daSbmercer return len + 1;
990c03f87daSbmercer }
991c03f87daSbmercer }
992c03f87daSbmercer
993c03f87daSbmercer if (len > 0)
994c03f87daSbmercer memcpy(buf, data, min(len, buflen));
995c03f87daSbmercer return len;
996c03f87daSbmercer }
997c3d9d24eSkettenis
9983ff54624Skettenis int
OF_getpropbool(int handle,char * prop)9993ff54624Skettenis OF_getpropbool(int handle, char *prop)
10003ff54624Skettenis {
10013ff54624Skettenis void *node = (char *)tree.header + handle;
10023ff54624Skettenis char *data;
10033ff54624Skettenis
10043ff54624Skettenis return (fdt_node_property(node, prop, &data) >= 0);
10053ff54624Skettenis }
10063ff54624Skettenis
1007bf5fea1eSkettenis uint32_t
OF_getpropint(int handle,char * prop,uint32_t defval)1008bf5fea1eSkettenis OF_getpropint(int handle, char *prop, uint32_t defval)
1009bf5fea1eSkettenis {
1010bf5fea1eSkettenis uint32_t val;
1011bf5fea1eSkettenis int len;
1012bf5fea1eSkettenis
1013bf5fea1eSkettenis len = OF_getprop(handle, prop, &val, sizeof(val));
1014bf5fea1eSkettenis if (len != sizeof(val))
1015bf5fea1eSkettenis return defval;
1016bf5fea1eSkettenis
1017bf5fea1eSkettenis return betoh32(val);
1018bf5fea1eSkettenis }
1019bf5fea1eSkettenis
1020bf5fea1eSkettenis int
OF_getpropintarray(int handle,char * prop,uint32_t * buf,int buflen)1021bf5fea1eSkettenis OF_getpropintarray(int handle, char *prop, uint32_t *buf, int buflen)
1022bf5fea1eSkettenis {
1023bf5fea1eSkettenis int len;
1024bf5fea1eSkettenis int i;
1025bf5fea1eSkettenis
1026bf5fea1eSkettenis len = OF_getprop(handle, prop, buf, buflen);
1027bf5fea1eSkettenis if (len < 0 || (len % sizeof(uint32_t)))
1028bf5fea1eSkettenis return -1;
1029bf5fea1eSkettenis
10303685e9f5Skettenis for (i = 0; i < min(len, buflen) / sizeof(uint32_t); i++)
1031bf5fea1eSkettenis buf[i] = betoh32(buf[i]);
1032bf5fea1eSkettenis
1033bf5fea1eSkettenis return len;
1034bf5fea1eSkettenis }
1035bf5fea1eSkettenis
10362e72ff40Skettenis uint64_t
OF_getpropint64(int handle,char * prop,uint64_t defval)10372e72ff40Skettenis OF_getpropint64(int handle, char *prop, uint64_t defval)
10382e72ff40Skettenis {
10392e72ff40Skettenis uint64_t val;
10402e72ff40Skettenis int len;
10412e72ff40Skettenis
10422e72ff40Skettenis len = OF_getprop(handle, prop, &val, sizeof(val));
10432e72ff40Skettenis if (len != sizeof(val))
10442e72ff40Skettenis return defval;
10452e72ff40Skettenis
10462e72ff40Skettenis return betoh64(val);
10472e72ff40Skettenis }
10482e72ff40Skettenis
1049c3d9d24eSkettenis int
OF_getpropint64array(int handle,char * prop,uint64_t * buf,int buflen)1050196daab3Sgkoehler OF_getpropint64array(int handle, char *prop, uint64_t *buf, int buflen)
1051196daab3Sgkoehler {
1052196daab3Sgkoehler int len;
1053196daab3Sgkoehler int i;
1054196daab3Sgkoehler
1055196daab3Sgkoehler len = OF_getprop(handle, prop, buf, buflen);
1056196daab3Sgkoehler if (len < 0 || (len % sizeof(uint64_t)))
1057196daab3Sgkoehler return -1;
1058196daab3Sgkoehler
10595fece840Sjasper for (i = 0; i < min(len, buflen) / sizeof(uint64_t); i++)
1060196daab3Sgkoehler buf[i] = betoh64(buf[i]);
1061196daab3Sgkoehler
1062196daab3Sgkoehler return len;
1063196daab3Sgkoehler }
1064196daab3Sgkoehler
1065196daab3Sgkoehler int
OF_nextprop(int handle,char * prop,void * nextprop)1066d571cddaSkettenis OF_nextprop(int handle, char *prop, void *nextprop)
1067d571cddaSkettenis {
1068d571cddaSkettenis void *node = (char *)tree.header + handle;
1069d571cddaSkettenis char *data;
1070d571cddaSkettenis
1071ed9c5852Skettenis if (fdt_node_property(node, "name", &data) == -1) {
1072d571cddaSkettenis if (strcmp(prop, "") == 0)
107344d8669dSkettenis return strlcpy(nextprop, "name", OFMAXPARAM);
1074d571cddaSkettenis if (strcmp(prop, "name") == 0)
1075d571cddaSkettenis prop = "";
1076d571cddaSkettenis }
1077d571cddaSkettenis
1078d571cddaSkettenis if (fdt_next_property(node, prop, &data))
107944d8669dSkettenis return strlcpy(nextprop, data, OFMAXPARAM);
1080d571cddaSkettenis return -1;
1081d571cddaSkettenis }
1082d571cddaSkettenis
1083d571cddaSkettenis int
OF_is_compatible(int handle,const char * name)1084c3d9d24eSkettenis OF_is_compatible(int handle, const char *name)
1085c3d9d24eSkettenis {
1086c3d9d24eSkettenis void *node = (char *)tree.header + handle;
1087557e6971Sjsg return (fdt_is_compatible(node, name));
1088c3d9d24eSkettenis }
1089c3d9d24eSkettenis
109050946bcbSkettenis int
OF_getindex(int handle,const char * entry,const char * prop)109150946bcbSkettenis OF_getindex(int handle, const char *entry, const char *prop)
109250946bcbSkettenis {
109350946bcbSkettenis char *names;
109450946bcbSkettenis char *name;
109550946bcbSkettenis char *end;
109650946bcbSkettenis int idx = 0;
109750946bcbSkettenis int len;
109850946bcbSkettenis
109950946bcbSkettenis if (entry == NULL)
110050946bcbSkettenis return 0;
110150946bcbSkettenis
110250946bcbSkettenis len = OF_getproplen(handle, (char *)prop);
110350946bcbSkettenis if (len <= 0)
110450946bcbSkettenis return -1;
110550946bcbSkettenis
110650946bcbSkettenis names = malloc(len, M_TEMP, M_WAITOK);
110750946bcbSkettenis OF_getprop(handle, (char *)prop, names, len);
110850946bcbSkettenis end = names + len;
110950946bcbSkettenis name = names;
111050946bcbSkettenis while (name < end) {
111150946bcbSkettenis if (strcmp(name, entry) == 0) {
111250946bcbSkettenis free(names, M_TEMP, len);
111350946bcbSkettenis return idx;
111450946bcbSkettenis }
111550946bcbSkettenis name += strlen(name) + 1;
111650946bcbSkettenis idx++;
111750946bcbSkettenis }
111850946bcbSkettenis free(names, M_TEMP, len);
111950946bcbSkettenis return -1;
112050946bcbSkettenis }
1121