of: remove special case definition of of_read_ulong()
[deliverable/linux.git] / drivers / of / fdt.c
CommitLineData
e169cfbe
GL
1/*
2 * Functions for working with the Flattened Device Tree data format
3 *
4 * Copyright 2009 Benjamin Herrenschmidt, IBM Corp
5 * benh@kernel.crashing.org
6 *
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.
10 */
11
41f88009
GL
12#include <linux/kernel.h>
13#include <linux/lmb.h>
e169cfbe
GL
14#include <linux/of.h>
15#include <linux/of_fdt.h>
16
17struct boot_param_header *initial_boot_params;
18
19char *find_flat_dt_string(u32 offset)
20{
21 return ((char *)initial_boot_params) +
22 initial_boot_params->off_dt_strings + offset;
23}
c8cb7a59
GL
24
25/**
26 * of_scan_flat_dt - scan flattened tree blob and call callback on each.
27 * @it: callback function
28 * @data: context data pointer
29 *
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
32 * unflatten the tree
33 */
34int __init of_scan_flat_dt(int (*it)(unsigned long node,
35 const char *uname, int depth,
36 void *data),
37 void *data)
38{
39 unsigned long p = ((unsigned long)initial_boot_params) +
40 initial_boot_params->off_dt_struct;
41 int rc = 0;
42 int depth = -1;
43
44 do {
45 u32 tag = *((u32 *)p);
46 char *pathp;
47
48 p += 4;
49 if (tag == OF_DT_END_NODE) {
50 depth--;
51 continue;
52 }
53 if (tag == OF_DT_NOP)
54 continue;
55 if (tag == OF_DT_END)
56 break;
57 if (tag == OF_DT_PROP) {
58 u32 sz = *((u32 *)p);
59 p += 8;
60 if (initial_boot_params->version < 0x10)
61 p = _ALIGN(p, sz >= 8 ? 8 : 4);
62 p += sz;
63 p = _ALIGN(p, 4);
64 continue;
65 }
66 if (tag != OF_DT_BEGIN_NODE) {
67 pr_err("Invalid tag %x in flat device tree!\n", tag);
68 return -EINVAL;
69 }
70 depth++;
71 pathp = (char *)p;
72 p = _ALIGN(p + strlen(pathp) + 1, 4);
73 if ((*pathp) == '/') {
74 char *lp, *np;
75 for (lp = NULL, np = pathp; *np; np++)
76 if ((*np) == '/')
77 lp = np+1;
78 if (lp != NULL)
79 pathp = lp;
80 }
81 rc = it(p, pathp, depth, data);
82 if (rc != 0)
83 break;
84 } while (1);
85
86 return rc;
87}
819d2819
GL
88
89/**
90 * of_get_flat_dt_root - find the root node in the flat blob
91 */
92unsigned long __init of_get_flat_dt_root(void)
93{
94 unsigned long p = ((unsigned long)initial_boot_params) +
95 initial_boot_params->off_dt_struct;
96
97 while (*((u32 *)p) == OF_DT_NOP)
98 p += 4;
99 BUG_ON(*((u32 *)p) != OF_DT_BEGIN_NODE);
100 p += 4;
101 return _ALIGN(p + strlen((char *)p) + 1, 4);
102}
103
ca900cfa
GL
104/**
105 * of_get_flat_dt_prop - Given a node in the flat blob, return the property ptr
106 *
107 * This function can be used within scan_flattened_dt callback to get
108 * access to properties
109 */
110void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
111 unsigned long *size)
112{
113 unsigned long p = node;
114
115 do {
116 u32 tag = *((u32 *)p);
117 u32 sz, noff;
118 const char *nstr;
119
120 p += 4;
121 if (tag == OF_DT_NOP)
122 continue;
123 if (tag != OF_DT_PROP)
124 return NULL;
125
126 sz = *((u32 *)p);
127 noff = *((u32 *)(p + 4));
128 p += 8;
129 if (initial_boot_params->version < 0x10)
130 p = _ALIGN(p, sz >= 8 ? 8 : 4);
131
132 nstr = find_flat_dt_string(noff);
133 if (nstr == NULL) {
134 pr_warning("Can't find property index name !\n");
135 return NULL;
136 }
137 if (strcmp(name, nstr) == 0) {
138 if (size)
139 *size = sz;
140 return (void *)p;
141 }
142 p += sz;
143 p = _ALIGN(p, 4);
144 } while (1);
145}
146
00e38efd
GL
147/**
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.
151 */
152int __init of_flat_dt_is_compatible(unsigned long node, const char *compat)
153{
154 const char *cp;
155 unsigned long cplen, l;
156
157 cp = of_get_flat_dt_prop(node, "compatible", &cplen);
158 if (cp == NULL)
159 return 0;
160 while (cplen > 0) {
161 if (strncasecmp(cp, compat, strlen(compat)) == 0)
162 return 1;
163 l = strlen(cp) + 1;
164 cp += l;
165 cplen -= l;
166 }
167
168 return 0;
169}
170
bbd33931
GL
171static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size,
172 unsigned long align)
173{
174 void *res;
175
176 *mem = _ALIGN(*mem, align);
177 res = (void *)*mem;
178 *mem += size;
179
180 return res;
181}
182
183/**
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.
189 */
190unsigned long __init unflatten_dt_node(unsigned long mem,
191 unsigned long *p,
192 struct device_node *dad,
193 struct device_node ***allnextpp,
194 unsigned long fpsize)
195{
196 struct device_node *np;
197 struct property *pp, **prev_pp = NULL;
198 char *pathp;
199 u32 tag;
200 unsigned int l, allocl;
201 int has_name = 0;
202 int new_format = 0;
203
204 tag = *((u32 *)(*p));
205 if (tag != OF_DT_BEGIN_NODE) {
206 pr_err("Weird tag at start of node: %x\n", tag);
207 return mem;
208 }
209 *p += 4;
210 pathp = (char *)*p;
211 l = allocl = strlen(pathp) + 1;
212 *p = _ALIGN(*p + l, 4);
213
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
217 * not '/'.
218 */
219 if ((*pathp) != '/') {
220 new_format = 1;
221 if (fpsize == 0) {
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
226 */
227 fpsize = 1;
228 allocl = 2;
229 } else {
230 /* account for '/' and path size minus terminal 0
231 * already in 'l'
232 */
233 fpsize += l;
234 allocl = fpsize;
235 }
236 }
237
238 np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
239 __alignof__(struct device_node));
240 if (allnextpp) {
241 memset(np, 0, sizeof(*np));
242 np->full_name = ((char *)np) + sizeof(struct device_node);
243 if (new_format) {
244 char *fn = np->full_name;
245 /* rebuild full path for new format */
246 if (dad && dad->parent) {
247 strcpy(fn, dad->full_name);
248#ifdef DEBUG
249 if ((strlen(fn) + l + 1) != allocl) {
250 pr_debug("%s: p: %d, l: %d, a: %d\n",
251 pathp, (int)strlen(fn),
252 l, allocl);
253 }
254#endif
255 fn += strlen(fn);
256 }
257 *(fn++) = '/';
258 memcpy(fn, pathp, l);
259 } else
260 memcpy(np->full_name, pathp, l);
261 prev_pp = &np->properties;
262 **allnextpp = np;
263 *allnextpp = &np->allnext;
264 if (dad != NULL) {
265 np->parent = dad;
266 /* we temporarily use the next field as `last_child'*/
267 if (dad->next == NULL)
268 dad->child = np;
269 else
270 dad->next->sibling = np;
271 dad->next = np;
272 }
273 kref_init(&np->kref);
274 }
275 while (1) {
276 u32 sz, noff;
277 char *pname;
278
279 tag = *((u32 *)(*p));
280 if (tag == OF_DT_NOP) {
281 *p += 4;
282 continue;
283 }
284 if (tag != OF_DT_PROP)
285 break;
286 *p += 4;
287 sz = *((u32 *)(*p));
288 noff = *((u32 *)((*p) + 4));
289 *p += 8;
290 if (initial_boot_params->version < 0x10)
291 *p = _ALIGN(*p, sz >= 8 ? 8 : 4);
292
293 pname = find_flat_dt_string(noff);
294 if (pname == NULL) {
295 pr_info("Can't find property name in list !\n");
296 break;
297 }
298 if (strcmp(pname, "name") == 0)
299 has_name = 1;
300 l = strlen(pname) + 1;
301 pp = unflatten_dt_alloc(&mem, sizeof(struct property),
302 __alignof__(struct property));
303 if (allnextpp) {
304 if (strcmp(pname, "linux,phandle") == 0) {
305 np->node = *((u32 *)*p);
306 if (np->linux_phandle == 0)
307 np->linux_phandle = np->node;
308 }
309 if (strcmp(pname, "ibm,phandle") == 0)
310 np->linux_phandle = *((u32 *)*p);
311 pp->name = pname;
312 pp->length = sz;
313 pp->value = (void *)*p;
314 *prev_pp = pp;
315 prev_pp = &pp->next;
316 }
317 *p = _ALIGN((*p) + sz, 4);
318 }
319 /* with version 0x10 we may not have the name property, recreate
320 * it here from the unit name if absent
321 */
322 if (!has_name) {
323 char *p1 = pathp, *ps = pathp, *pa = NULL;
324 int sz;
325
326 while (*p1) {
327 if ((*p1) == '@')
328 pa = p1;
329 if ((*p1) == '/')
330 ps = p1 + 1;
331 p1++;
332 }
333 if (pa < ps)
334 pa = p1;
335 sz = (pa - ps) + 1;
336 pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
337 __alignof__(struct property));
338 if (allnextpp) {
339 pp->name = "name";
340 pp->length = sz;
341 pp->value = pp + 1;
342 *prev_pp = pp;
343 prev_pp = &pp->next;
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,
347 (char *)pp->value);
348 }
349 }
350 if (allnextpp) {
351 *prev_pp = NULL;
352 np->name = of_get_property(np, "name", NULL);
353 np->type = of_get_property(np, "device_type", NULL);
354
355 if (!np->name)
356 np->name = "<NULL>";
357 if (!np->type)
358 np->type = "<NULL>";
359 }
360 while (tag == OF_DT_BEGIN_NODE) {
361 mem = unflatten_dt_node(mem, p, np, allnextpp, fpsize);
362 tag = *((u32 *)(*p));
363 }
364 if (tag != OF_DT_END_NODE) {
365 pr_err("Weird tag at end of node: %x\n", tag);
366 return mem;
367 }
368 *p += 4;
369 return mem;
370}
41f88009
GL
371
372/**
373 * unflatten_device_tree - create tree of device_nodes from flat blob
374 *
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
378 * can be used.
379 */
380void __init unflatten_device_tree(void)
381{
382 unsigned long start, mem, size;
383 struct device_node **allnextp = &allnodes;
384
385 pr_debug(" -> unflatten_device_tree()\n");
386
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;
392
393 pr_debug(" size is %lx, allocating...\n", size);
394
395 /* Allocate memory for the expanded device tree */
396 mem = lmb_alloc(size + 4, __alignof__(struct device_node));
397 mem = (unsigned long) __va(mem);
398
399 ((u32 *)mem)[size / 4] = 0xdeadbeef;
400
401 pr_debug(" unflattening %lx...\n", mem);
402
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]);
412 *allnextp = NULL;
413
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");
418
419 pr_debug(" <- unflatten_device_tree()\n");
420}
This page took 0.040762 seconds and 5 git commands to generate.