]> bbs.cooldavid.org Git - net-next-2.6.git/blame - drivers/of/fdt.c
of/flattree: Merge unflatten_dt_node
[net-next-2.6.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
12#include <linux/of.h>
13#include <linux/of_fdt.h>
14
15struct boot_param_header *initial_boot_params;
16
17char *find_flat_dt_string(u32 offset)
18{
19 return ((char *)initial_boot_params) +
20 initial_boot_params->off_dt_strings + offset;
21}
c8cb7a59
GL
22
23/**
24 * of_scan_flat_dt - scan flattened tree blob and call callback on each.
25 * @it: callback function
26 * @data: context data pointer
27 *
28 * This function is used to scan the flattened device-tree, it is
29 * used to extract the memory information at boot before we can
30 * unflatten the tree
31 */
32int __init of_scan_flat_dt(int (*it)(unsigned long node,
33 const char *uname, int depth,
34 void *data),
35 void *data)
36{
37 unsigned long p = ((unsigned long)initial_boot_params) +
38 initial_boot_params->off_dt_struct;
39 int rc = 0;
40 int depth = -1;
41
42 do {
43 u32 tag = *((u32 *)p);
44 char *pathp;
45
46 p += 4;
47 if (tag == OF_DT_END_NODE) {
48 depth--;
49 continue;
50 }
51 if (tag == OF_DT_NOP)
52 continue;
53 if (tag == OF_DT_END)
54 break;
55 if (tag == OF_DT_PROP) {
56 u32 sz = *((u32 *)p);
57 p += 8;
58 if (initial_boot_params->version < 0x10)
59 p = _ALIGN(p, sz >= 8 ? 8 : 4);
60 p += sz;
61 p = _ALIGN(p, 4);
62 continue;
63 }
64 if (tag != OF_DT_BEGIN_NODE) {
65 pr_err("Invalid tag %x in flat device tree!\n", tag);
66 return -EINVAL;
67 }
68 depth++;
69 pathp = (char *)p;
70 p = _ALIGN(p + strlen(pathp) + 1, 4);
71 if ((*pathp) == '/') {
72 char *lp, *np;
73 for (lp = NULL, np = pathp; *np; np++)
74 if ((*np) == '/')
75 lp = np+1;
76 if (lp != NULL)
77 pathp = lp;
78 }
79 rc = it(p, pathp, depth, data);
80 if (rc != 0)
81 break;
82 } while (1);
83
84 return rc;
85}
819d2819
GL
86
87/**
88 * of_get_flat_dt_root - find the root node in the flat blob
89 */
90unsigned long __init of_get_flat_dt_root(void)
91{
92 unsigned long p = ((unsigned long)initial_boot_params) +
93 initial_boot_params->off_dt_struct;
94
95 while (*((u32 *)p) == OF_DT_NOP)
96 p += 4;
97 BUG_ON(*((u32 *)p) != OF_DT_BEGIN_NODE);
98 p += 4;
99 return _ALIGN(p + strlen((char *)p) + 1, 4);
100}
101
ca900cfa
GL
102/**
103 * of_get_flat_dt_prop - Given a node in the flat blob, return the property ptr
104 *
105 * This function can be used within scan_flattened_dt callback to get
106 * access to properties
107 */
108void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
109 unsigned long *size)
110{
111 unsigned long p = node;
112
113 do {
114 u32 tag = *((u32 *)p);
115 u32 sz, noff;
116 const char *nstr;
117
118 p += 4;
119 if (tag == OF_DT_NOP)
120 continue;
121 if (tag != OF_DT_PROP)
122 return NULL;
123
124 sz = *((u32 *)p);
125 noff = *((u32 *)(p + 4));
126 p += 8;
127 if (initial_boot_params->version < 0x10)
128 p = _ALIGN(p, sz >= 8 ? 8 : 4);
129
130 nstr = find_flat_dt_string(noff);
131 if (nstr == NULL) {
132 pr_warning("Can't find property index name !\n");
133 return NULL;
134 }
135 if (strcmp(name, nstr) == 0) {
136 if (size)
137 *size = sz;
138 return (void *)p;
139 }
140 p += sz;
141 p = _ALIGN(p, 4);
142 } while (1);
143}
144
00e38efd
GL
145/**
146 * of_flat_dt_is_compatible - Return true if given node has compat in compatible list
147 * @node: node to test
148 * @compat: compatible string to compare with compatible list.
149 */
150int __init of_flat_dt_is_compatible(unsigned long node, const char *compat)
151{
152 const char *cp;
153 unsigned long cplen, l;
154
155 cp = of_get_flat_dt_prop(node, "compatible", &cplen);
156 if (cp == NULL)
157 return 0;
158 while (cplen > 0) {
159 if (strncasecmp(cp, compat, strlen(compat)) == 0)
160 return 1;
161 l = strlen(cp) + 1;
162 cp += l;
163 cplen -= l;
164 }
165
166 return 0;
167}
168
bbd33931
GL
169static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size,
170 unsigned long align)
171{
172 void *res;
173
174 *mem = _ALIGN(*mem, align);
175 res = (void *)*mem;
176 *mem += size;
177
178 return res;
179}
180
181/**
182 * unflatten_dt_node - Alloc and populate a device_node from the flat tree
183 * @p: pointer to node in flat tree
184 * @dad: Parent struct device_node
185 * @allnextpp: pointer to ->allnext from last allocated device_node
186 * @fpsize: Size of the node path up at the current depth.
187 */
188unsigned long __init unflatten_dt_node(unsigned long mem,
189 unsigned long *p,
190 struct device_node *dad,
191 struct device_node ***allnextpp,
192 unsigned long fpsize)
193{
194 struct device_node *np;
195 struct property *pp, **prev_pp = NULL;
196 char *pathp;
197 u32 tag;
198 unsigned int l, allocl;
199 int has_name = 0;
200 int new_format = 0;
201
202 tag = *((u32 *)(*p));
203 if (tag != OF_DT_BEGIN_NODE) {
204 pr_err("Weird tag at start of node: %x\n", tag);
205 return mem;
206 }
207 *p += 4;
208 pathp = (char *)*p;
209 l = allocl = strlen(pathp) + 1;
210 *p = _ALIGN(*p + l, 4);
211
212 /* version 0x10 has a more compact unit name here instead of the full
213 * path. we accumulate the full path size using "fpsize", we'll rebuild
214 * it later. We detect this because the first character of the name is
215 * not '/'.
216 */
217 if ((*pathp) != '/') {
218 new_format = 1;
219 if (fpsize == 0) {
220 /* root node: special case. fpsize accounts for path
221 * plus terminating zero. root node only has '/', so
222 * fpsize should be 2, but we want to avoid the first
223 * level nodes to have two '/' so we use fpsize 1 here
224 */
225 fpsize = 1;
226 allocl = 2;
227 } else {
228 /* account for '/' and path size minus terminal 0
229 * already in 'l'
230 */
231 fpsize += l;
232 allocl = fpsize;
233 }
234 }
235
236 np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
237 __alignof__(struct device_node));
238 if (allnextpp) {
239 memset(np, 0, sizeof(*np));
240 np->full_name = ((char *)np) + sizeof(struct device_node);
241 if (new_format) {
242 char *fn = np->full_name;
243 /* rebuild full path for new format */
244 if (dad && dad->parent) {
245 strcpy(fn, dad->full_name);
246#ifdef DEBUG
247 if ((strlen(fn) + l + 1) != allocl) {
248 pr_debug("%s: p: %d, l: %d, a: %d\n",
249 pathp, (int)strlen(fn),
250 l, allocl);
251 }
252#endif
253 fn += strlen(fn);
254 }
255 *(fn++) = '/';
256 memcpy(fn, pathp, l);
257 } else
258 memcpy(np->full_name, pathp, l);
259 prev_pp = &np->properties;
260 **allnextpp = np;
261 *allnextpp = &np->allnext;
262 if (dad != NULL) {
263 np->parent = dad;
264 /* we temporarily use the next field as `last_child'*/
265 if (dad->next == NULL)
266 dad->child = np;
267 else
268 dad->next->sibling = np;
269 dad->next = np;
270 }
271 kref_init(&np->kref);
272 }
273 while (1) {
274 u32 sz, noff;
275 char *pname;
276
277 tag = *((u32 *)(*p));
278 if (tag == OF_DT_NOP) {
279 *p += 4;
280 continue;
281 }
282 if (tag != OF_DT_PROP)
283 break;
284 *p += 4;
285 sz = *((u32 *)(*p));
286 noff = *((u32 *)((*p) + 4));
287 *p += 8;
288 if (initial_boot_params->version < 0x10)
289 *p = _ALIGN(*p, sz >= 8 ? 8 : 4);
290
291 pname = find_flat_dt_string(noff);
292 if (pname == NULL) {
293 pr_info("Can't find property name in list !\n");
294 break;
295 }
296 if (strcmp(pname, "name") == 0)
297 has_name = 1;
298 l = strlen(pname) + 1;
299 pp = unflatten_dt_alloc(&mem, sizeof(struct property),
300 __alignof__(struct property));
301 if (allnextpp) {
302 if (strcmp(pname, "linux,phandle") == 0) {
303 np->node = *((u32 *)*p);
304 if (np->linux_phandle == 0)
305 np->linux_phandle = np->node;
306 }
307 if (strcmp(pname, "ibm,phandle") == 0)
308 np->linux_phandle = *((u32 *)*p);
309 pp->name = pname;
310 pp->length = sz;
311 pp->value = (void *)*p;
312 *prev_pp = pp;
313 prev_pp = &pp->next;
314 }
315 *p = _ALIGN((*p) + sz, 4);
316 }
317 /* with version 0x10 we may not have the name property, recreate
318 * it here from the unit name if absent
319 */
320 if (!has_name) {
321 char *p1 = pathp, *ps = pathp, *pa = NULL;
322 int sz;
323
324 while (*p1) {
325 if ((*p1) == '@')
326 pa = p1;
327 if ((*p1) == '/')
328 ps = p1 + 1;
329 p1++;
330 }
331 if (pa < ps)
332 pa = p1;
333 sz = (pa - ps) + 1;
334 pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
335 __alignof__(struct property));
336 if (allnextpp) {
337 pp->name = "name";
338 pp->length = sz;
339 pp->value = pp + 1;
340 *prev_pp = pp;
341 prev_pp = &pp->next;
342 memcpy(pp->value, ps, sz - 1);
343 ((char *)pp->value)[sz - 1] = 0;
344 pr_debug("fixed up name for %s -> %s\n", pathp,
345 (char *)pp->value);
346 }
347 }
348 if (allnextpp) {
349 *prev_pp = NULL;
350 np->name = of_get_property(np, "name", NULL);
351 np->type = of_get_property(np, "device_type", NULL);
352
353 if (!np->name)
354 np->name = "<NULL>";
355 if (!np->type)
356 np->type = "<NULL>";
357 }
358 while (tag == OF_DT_BEGIN_NODE) {
359 mem = unflatten_dt_node(mem, p, np, allnextpp, fpsize);
360 tag = *((u32 *)(*p));
361 }
362 if (tag != OF_DT_END_NODE) {
363 pr_err("Weird tag at end of node: %x\n", tag);
364 return mem;
365 }
366 *p += 4;
367 return mem;
368}