2 * Functions for working with the Flattened Device Tree data format
4 * Copyright 2009 Benjamin Herrenschmidt, IBM Corp
5 * benh@kernel.crashing.org
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * version 2 as published by the Free Software Foundation.
12 #include <linux/kernel.h>
13 #include <linux/lmb.h>
15 #include <linux/of_fdt.h>
17 struct boot_param_header *initial_boot_params;
19 char *find_flat_dt_string(u32 offset)
21 return ((char *)initial_boot_params) +
22 initial_boot_params->off_dt_strings + offset;
26 * of_scan_flat_dt - scan flattened tree blob and call callback on each.
27 * @it: callback function
28 * @data: context data pointer
30 * This function is used to scan the flattened device-tree, it is
31 * used to extract the memory information at boot before we can
34 int __init of_scan_flat_dt(int (*it)(unsigned long node,
35 const char *uname, int depth,
39 unsigned long p = ((unsigned long)initial_boot_params) +
40 initial_boot_params->off_dt_struct;
45 u32 tag = *((u32 *)p);
49 if (tag == OF_DT_END_NODE) {
57 if (tag == OF_DT_PROP) {
60 if (initial_boot_params->version < 0x10)
61 p = _ALIGN(p, sz >= 8 ? 8 : 4);
66 if (tag != OF_DT_BEGIN_NODE) {
67 pr_err("Invalid tag %x in flat device tree!\n", tag);
72 p = _ALIGN(p + strlen(pathp) + 1, 4);
73 if ((*pathp) == '/') {
75 for (lp = NULL, np = pathp; *np; np++)
81 rc = it(p, pathp, depth, data);
90 * of_get_flat_dt_root - find the root node in the flat blob
92 unsigned long __init of_get_flat_dt_root(void)
94 unsigned long p = ((unsigned long)initial_boot_params) +
95 initial_boot_params->off_dt_struct;
97 while (*((u32 *)p) == OF_DT_NOP)
99 BUG_ON(*((u32 *)p) != OF_DT_BEGIN_NODE);
101 return _ALIGN(p + strlen((char *)p) + 1, 4);
105 * of_get_flat_dt_prop - Given a node in the flat blob, return the property ptr
107 * This function can be used within scan_flattened_dt callback to get
108 * access to properties
110 void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
113 unsigned long p = node;
116 u32 tag = *((u32 *)p);
121 if (tag == OF_DT_NOP)
123 if (tag != OF_DT_PROP)
127 noff = *((u32 *)(p + 4));
129 if (initial_boot_params->version < 0x10)
130 p = _ALIGN(p, sz >= 8 ? 8 : 4);
132 nstr = find_flat_dt_string(noff);
134 pr_warning("Can't find property index name !\n");
137 if (strcmp(name, nstr) == 0) {
148 * of_flat_dt_is_compatible - Return true if given node has compat in compatible list
149 * @node: node to test
150 * @compat: compatible string to compare with compatible list.
152 int __init of_flat_dt_is_compatible(unsigned long node, const char *compat)
155 unsigned long cplen, l;
157 cp = of_get_flat_dt_prop(node, "compatible", &cplen);
161 if (strncasecmp(cp, compat, strlen(compat)) == 0)
171 static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size,
176 *mem = _ALIGN(*mem, align);
184 * unflatten_dt_node - Alloc and populate a device_node from the flat tree
185 * @p: pointer to node in flat tree
186 * @dad: Parent struct device_node
187 * @allnextpp: pointer to ->allnext from last allocated device_node
188 * @fpsize: Size of the node path up at the current depth.
190 unsigned long __init unflatten_dt_node(unsigned long mem,
192 struct device_node *dad,
193 struct device_node ***allnextpp,
194 unsigned long fpsize)
196 struct device_node *np;
197 struct property *pp, **prev_pp = NULL;
200 unsigned int l, allocl;
204 tag = *((u32 *)(*p));
205 if (tag != OF_DT_BEGIN_NODE) {
206 pr_err("Weird tag at start of node: %x\n", tag);
211 l = allocl = strlen(pathp) + 1;
212 *p = _ALIGN(*p + l, 4);
214 /* version 0x10 has a more compact unit name here instead of the full
215 * path. we accumulate the full path size using "fpsize", we'll rebuild
216 * it later. We detect this because the first character of the name is
219 if ((*pathp) != '/') {
222 /* root node: special case. fpsize accounts for path
223 * plus terminating zero. root node only has '/', so
224 * fpsize should be 2, but we want to avoid the first
225 * level nodes to have two '/' so we use fpsize 1 here
230 /* account for '/' and path size minus terminal 0
238 np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
239 __alignof__(struct device_node));
241 memset(np, 0, sizeof(*np));
242 np->full_name = ((char *)np) + sizeof(struct device_node);
244 char *fn = np->full_name;
245 /* rebuild full path for new format */
246 if (dad && dad->parent) {
247 strcpy(fn, dad->full_name);
249 if ((strlen(fn) + l + 1) != allocl) {
250 pr_debug("%s: p: %d, l: %d, a: %d\n",
251 pathp, (int)strlen(fn),
258 memcpy(fn, pathp, l);
260 memcpy(np->full_name, pathp, l);
261 prev_pp = &np->properties;
263 *allnextpp = &np->allnext;
266 /* we temporarily use the next field as `last_child'*/
267 if (dad->next == NULL)
270 dad->next->sibling = np;
273 kref_init(&np->kref);
279 tag = *((u32 *)(*p));
280 if (tag == OF_DT_NOP) {
284 if (tag != OF_DT_PROP)
288 noff = *((u32 *)((*p) + 4));
290 if (initial_boot_params->version < 0x10)
291 *p = _ALIGN(*p, sz >= 8 ? 8 : 4);
293 pname = find_flat_dt_string(noff);
295 pr_info("Can't find property name in list !\n");
298 if (strcmp(pname, "name") == 0)
300 l = strlen(pname) + 1;
301 pp = unflatten_dt_alloc(&mem, sizeof(struct property),
302 __alignof__(struct property));
304 if (strcmp(pname, "linux,phandle") == 0) {
305 np->node = *((u32 *)*p);
306 if (np->linux_phandle == 0)
307 np->linux_phandle = np->node;
309 if (strcmp(pname, "ibm,phandle") == 0)
310 np->linux_phandle = *((u32 *)*p);
313 pp->value = (void *)*p;
317 *p = _ALIGN((*p) + sz, 4);
319 /* with version 0x10 we may not have the name property, recreate
320 * it here from the unit name if absent
323 char *p1 = pathp, *ps = pathp, *pa = NULL;
336 pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
337 __alignof__(struct property));
344 memcpy(pp->value, ps, sz - 1);
345 ((char *)pp->value)[sz - 1] = 0;
346 pr_debug("fixed up name for %s -> %s\n", pathp,
352 np->name = of_get_property(np, "name", NULL);
353 np->type = of_get_property(np, "device_type", NULL);
360 while (tag == OF_DT_BEGIN_NODE) {
361 mem = unflatten_dt_node(mem, p, np, allnextpp, fpsize);
362 tag = *((u32 *)(*p));
364 if (tag != OF_DT_END_NODE) {
365 pr_err("Weird tag at end of node: %x\n", tag);
373 * unflatten_device_tree - create tree of device_nodes from flat blob
375 * unflattens the device-tree passed by the firmware, creating the
376 * tree of struct device_node. It also fills the "name" and "type"
377 * pointers of the nodes so the normal device-tree walking functions
380 void __init unflatten_device_tree(void)
382 unsigned long start, mem, size;
383 struct device_node **allnextp = &allnodes;
385 pr_debug(" -> unflatten_device_tree()\n");
387 /* First pass, scan for size */
388 start = ((unsigned long)initial_boot_params) +
389 initial_boot_params->off_dt_struct;
390 size = unflatten_dt_node(0, &start, NULL, NULL, 0);
391 size = (size | 3) + 1;
393 pr_debug(" size is %lx, allocating...\n", size);
395 /* Allocate memory for the expanded device tree */
396 mem = lmb_alloc(size + 4, __alignof__(struct device_node));
397 mem = (unsigned long) __va(mem);
399 ((u32 *)mem)[size / 4] = 0xdeadbeef;
401 pr_debug(" unflattening %lx...\n", mem);
403 /* Second pass, do actual unflattening */
404 start = ((unsigned long)initial_boot_params) +
405 initial_boot_params->off_dt_struct;
406 unflatten_dt_node(mem, &start, NULL, &allnextp, 0);
407 if (*((u32 *)start) != OF_DT_END)
408 pr_warning("Weird tag at end of tree: %08x\n", *((u32 *)start));
409 if (((u32 *)mem)[size / 4] != 0xdeadbeef)
410 pr_warning("End of tree marker overwritten: %08x\n",
411 ((u32 *)mem)[size / 4]);
414 /* Get pointer to OF "/chosen" node for use everywhere */
415 of_chosen = of_find_node_by_path("/chosen");
416 if (of_chosen == NULL)
417 of_chosen = of_find_node_by_path("/chosen@0");
419 pr_debug(" <- unflatten_device_tree()\n");