5cdd958db9af6210d40dd9bce06e56b326c17d49
[safe/jmp/linux-2.6] / drivers / of / fdt.c
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
15 struct boot_param_header *initial_boot_params;
16
17 char *find_flat_dt_string(u32 offset)
18 {
19         return ((char *)initial_boot_params) +
20                 initial_boot_params->off_dt_strings + offset;
21 }
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  */
32 int __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 }
86
87 /**
88  * of_get_flat_dt_root - find the root node in the flat blob
89  */
90 unsigned 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
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  */
108 void *__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
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  */
150 int __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