diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
commit | c71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch) | |
tree | ecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /drivers/of/pdt.c | |
parent | ea53c912f8a86a8567697115b6a0d8152beee5c8 (diff) | |
parent | 6a00f206debf8a5c8899055726ad127dbeeed098 (diff) |
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts:
litmus/sched_cedf.c
Diffstat (limited to 'drivers/of/pdt.c')
-rw-r--r-- | drivers/of/pdt.c | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c new file mode 100644 index 000000000000..4d87b5dc9284 --- /dev/null +++ b/drivers/of/pdt.c | |||
@@ -0,0 +1,248 @@ | |||
1 | /* pdt.c: OF PROM device tree support code. | ||
2 | * | ||
3 | * Paul Mackerras August 1996. | ||
4 | * Copyright (C) 1996-2005 Paul Mackerras. | ||
5 | * | ||
6 | * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. | ||
7 | * {engebret|bergner}@us.ibm.com | ||
8 | * | ||
9 | * Adapted for sparc by David S. Miller davem@davemloft.net | ||
10 | * Adapted for multiple architectures by Andres Salomon <dilinger@queued.net> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version | ||
15 | * 2 of the License, or (at your option) any later version. | ||
16 | */ | ||
17 | |||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/errno.h> | ||
21 | #include <linux/mutex.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/of.h> | ||
24 | #include <linux/of_pdt.h> | ||
25 | #include <asm/prom.h> | ||
26 | |||
27 | static struct of_pdt_ops *of_pdt_prom_ops __initdata; | ||
28 | |||
29 | void __initdata (*of_pdt_build_more)(struct device_node *dp, | ||
30 | struct device_node ***nextp); | ||
31 | |||
32 | #if defined(CONFIG_SPARC) | ||
33 | unsigned int of_pdt_unique_id __initdata; | ||
34 | |||
35 | #define of_pdt_incr_unique_id(p) do { \ | ||
36 | (p)->unique_id = of_pdt_unique_id++; \ | ||
37 | } while (0) | ||
38 | |||
39 | static char * __init of_pdt_build_full_name(struct device_node *dp) | ||
40 | { | ||
41 | int len, ourlen, plen; | ||
42 | char *n; | ||
43 | |||
44 | dp->path_component_name = build_path_component(dp); | ||
45 | |||
46 | plen = strlen(dp->parent->full_name); | ||
47 | ourlen = strlen(dp->path_component_name); | ||
48 | len = ourlen + plen + 2; | ||
49 | |||
50 | n = prom_early_alloc(len); | ||
51 | strcpy(n, dp->parent->full_name); | ||
52 | if (!of_node_is_root(dp->parent)) { | ||
53 | strcpy(n + plen, "/"); | ||
54 | plen++; | ||
55 | } | ||
56 | strcpy(n + plen, dp->path_component_name); | ||
57 | |||
58 | return n; | ||
59 | } | ||
60 | |||
61 | #else /* CONFIG_SPARC */ | ||
62 | |||
63 | static inline void of_pdt_incr_unique_id(void *p) { } | ||
64 | static inline void irq_trans_init(struct device_node *dp) { } | ||
65 | |||
66 | static char * __init of_pdt_build_full_name(struct device_node *dp) | ||
67 | { | ||
68 | static int failsafe_id = 0; /* for generating unique names on failure */ | ||
69 | char *buf; | ||
70 | int len; | ||
71 | |||
72 | if (of_pdt_prom_ops->pkg2path(dp->phandle, NULL, 0, &len)) | ||
73 | goto failsafe; | ||
74 | |||
75 | buf = prom_early_alloc(len + 1); | ||
76 | if (of_pdt_prom_ops->pkg2path(dp->phandle, buf, len, &len)) | ||
77 | goto failsafe; | ||
78 | return buf; | ||
79 | |||
80 | failsafe: | ||
81 | buf = prom_early_alloc(strlen(dp->parent->full_name) + | ||
82 | strlen(dp->name) + 16); | ||
83 | sprintf(buf, "%s/%s@unknown%i", | ||
84 | of_node_is_root(dp->parent) ? "" : dp->parent->full_name, | ||
85 | dp->name, failsafe_id++); | ||
86 | pr_err("%s: pkg2path failed; assigning %s\n", __func__, buf); | ||
87 | return buf; | ||
88 | } | ||
89 | |||
90 | #endif /* !CONFIG_SPARC */ | ||
91 | |||
92 | static struct property * __init of_pdt_build_one_prop(phandle node, char *prev, | ||
93 | char *special_name, | ||
94 | void *special_val, | ||
95 | int special_len) | ||
96 | { | ||
97 | static struct property *tmp = NULL; | ||
98 | struct property *p; | ||
99 | int err; | ||
100 | |||
101 | if (tmp) { | ||
102 | p = tmp; | ||
103 | memset(p, 0, sizeof(*p) + 32); | ||
104 | tmp = NULL; | ||
105 | } else { | ||
106 | p = prom_early_alloc(sizeof(struct property) + 32); | ||
107 | of_pdt_incr_unique_id(p); | ||
108 | } | ||
109 | |||
110 | p->name = (char *) (p + 1); | ||
111 | if (special_name) { | ||
112 | strcpy(p->name, special_name); | ||
113 | p->length = special_len; | ||
114 | p->value = prom_early_alloc(special_len); | ||
115 | memcpy(p->value, special_val, special_len); | ||
116 | } else { | ||
117 | err = of_pdt_prom_ops->nextprop(node, prev, p->name); | ||
118 | if (err) { | ||
119 | tmp = p; | ||
120 | return NULL; | ||
121 | } | ||
122 | p->length = of_pdt_prom_ops->getproplen(node, p->name); | ||
123 | if (p->length <= 0) { | ||
124 | p->length = 0; | ||
125 | } else { | ||
126 | int len; | ||
127 | |||
128 | p->value = prom_early_alloc(p->length + 1); | ||
129 | len = of_pdt_prom_ops->getproperty(node, p->name, | ||
130 | p->value, p->length); | ||
131 | if (len <= 0) | ||
132 | p->length = 0; | ||
133 | ((unsigned char *)p->value)[p->length] = '\0'; | ||
134 | } | ||
135 | } | ||
136 | return p; | ||
137 | } | ||
138 | |||
139 | static struct property * __init of_pdt_build_prop_list(phandle node) | ||
140 | { | ||
141 | struct property *head, *tail; | ||
142 | |||
143 | head = tail = of_pdt_build_one_prop(node, NULL, | ||
144 | ".node", &node, sizeof(node)); | ||
145 | |||
146 | tail->next = of_pdt_build_one_prop(node, NULL, NULL, NULL, 0); | ||
147 | tail = tail->next; | ||
148 | while(tail) { | ||
149 | tail->next = of_pdt_build_one_prop(node, tail->name, | ||
150 | NULL, NULL, 0); | ||
151 | tail = tail->next; | ||
152 | } | ||
153 | |||
154 | return head; | ||
155 | } | ||
156 | |||
157 | static char * __init of_pdt_get_one_property(phandle node, const char *name) | ||
158 | { | ||
159 | char *buf = "<NULL>"; | ||
160 | int len; | ||
161 | |||
162 | len = of_pdt_prom_ops->getproplen(node, name); | ||
163 | if (len > 0) { | ||
164 | buf = prom_early_alloc(len); | ||
165 | len = of_pdt_prom_ops->getproperty(node, name, buf, len); | ||
166 | } | ||
167 | |||
168 | return buf; | ||
169 | } | ||
170 | |||
171 | static struct device_node * __init of_pdt_create_node(phandle node, | ||
172 | struct device_node *parent) | ||
173 | { | ||
174 | struct device_node *dp; | ||
175 | |||
176 | if (!node) | ||
177 | return NULL; | ||
178 | |||
179 | dp = prom_early_alloc(sizeof(*dp)); | ||
180 | of_pdt_incr_unique_id(dp); | ||
181 | dp->parent = parent; | ||
182 | |||
183 | kref_init(&dp->kref); | ||
184 | |||
185 | dp->name = of_pdt_get_one_property(node, "name"); | ||
186 | dp->type = of_pdt_get_one_property(node, "device_type"); | ||
187 | dp->phandle = node; | ||
188 | |||
189 | dp->properties = of_pdt_build_prop_list(node); | ||
190 | |||
191 | irq_trans_init(dp); | ||
192 | |||
193 | return dp; | ||
194 | } | ||
195 | |||
196 | static struct device_node * __init of_pdt_build_tree(struct device_node *parent, | ||
197 | phandle node, | ||
198 | struct device_node ***nextp) | ||
199 | { | ||
200 | struct device_node *ret = NULL, *prev_sibling = NULL; | ||
201 | struct device_node *dp; | ||
202 | |||
203 | while (1) { | ||
204 | dp = of_pdt_create_node(node, parent); | ||
205 | if (!dp) | ||
206 | break; | ||
207 | |||
208 | if (prev_sibling) | ||
209 | prev_sibling->sibling = dp; | ||
210 | |||
211 | if (!ret) | ||
212 | ret = dp; | ||
213 | prev_sibling = dp; | ||
214 | |||
215 | *(*nextp) = dp; | ||
216 | *nextp = &dp->allnext; | ||
217 | |||
218 | dp->full_name = of_pdt_build_full_name(dp); | ||
219 | |||
220 | dp->child = of_pdt_build_tree(dp, | ||
221 | of_pdt_prom_ops->getchild(node), nextp); | ||
222 | |||
223 | if (of_pdt_build_more) | ||
224 | of_pdt_build_more(dp, nextp); | ||
225 | |||
226 | node = of_pdt_prom_ops->getsibling(node); | ||
227 | } | ||
228 | |||
229 | return ret; | ||
230 | } | ||
231 | |||
232 | void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops) | ||
233 | { | ||
234 | struct device_node **nextp; | ||
235 | |||
236 | BUG_ON(!ops); | ||
237 | of_pdt_prom_ops = ops; | ||
238 | |||
239 | allnodes = of_pdt_create_node(root_node, NULL); | ||
240 | #if defined(CONFIG_SPARC) | ||
241 | allnodes->path_component_name = ""; | ||
242 | #endif | ||
243 | allnodes->full_name = "/"; | ||
244 | |||
245 | nextp = &allnodes->allnext; | ||
246 | allnodes->child = of_pdt_build_tree(allnodes, | ||
247 | of_pdt_prom_ops->getchild(allnodes->phandle), &nextp); | ||
248 | } | ||