diff options
-rw-r--r-- | drivers/of/fdt.c | 249 |
1 files changed, 147 insertions, 102 deletions
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 5e897bfe6628..1b8c4ab0574d 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c | |||
@@ -161,39 +161,127 @@ static void *unflatten_dt_alloc(void **mem, unsigned long size, | |||
161 | return res; | 161 | return res; |
162 | } | 162 | } |
163 | 163 | ||
164 | /** | 164 | static void populate_properties(const void *blob, |
165 | * unflatten_dt_node - Alloc and populate a device_node from the flat tree | 165 | int offset, |
166 | * @blob: The parent device tree blob | 166 | void **mem, |
167 | * @mem: Memory chunk to use for allocating device nodes and properties | 167 | struct device_node *np, |
168 | * @poffset: pointer to node in flat tree | 168 | const char *nodename, |
169 | * @dad: Parent struct device_node | ||
170 | * @nodepp: The device_node tree created by the call | ||
171 | * @fpsize: Size of the node path up at the current depth. | ||
172 | * @dryrun: If true, do not allocate device nodes but still calculate needed | ||
173 | * memory size | ||
174 | */ | ||
175 | static void * unflatten_dt_node(const void *blob, | ||
176 | void *mem, | ||
177 | int *poffset, | ||
178 | struct device_node *dad, | ||
179 | struct device_node **nodepp, | ||
180 | unsigned long fpsize, | ||
181 | bool dryrun) | 169 | bool dryrun) |
182 | { | 170 | { |
183 | const __be32 *p; | 171 | struct property *pp, **pprev = NULL; |
172 | int cur; | ||
173 | bool has_name = false; | ||
174 | |||
175 | pprev = &np->properties; | ||
176 | for (cur = fdt_first_property_offset(blob, offset); | ||
177 | cur >= 0; | ||
178 | cur = fdt_next_property_offset(blob, cur)) { | ||
179 | const __be32 *val; | ||
180 | const char *pname; | ||
181 | u32 sz; | ||
182 | |||
183 | val = fdt_getprop_by_offset(blob, cur, &pname, &sz); | ||
184 | if (!val) { | ||
185 | pr_warn("%s: Cannot locate property at 0x%x\n", | ||
186 | __func__, cur); | ||
187 | continue; | ||
188 | } | ||
189 | |||
190 | if (!pname) { | ||
191 | pr_warn("%s: Cannot find property name at 0x%x\n", | ||
192 | __func__, cur); | ||
193 | continue; | ||
194 | } | ||
195 | |||
196 | if (!strcmp(pname, "name")) | ||
197 | has_name = true; | ||
198 | |||
199 | pp = unflatten_dt_alloc(mem, sizeof(struct property), | ||
200 | __alignof__(struct property)); | ||
201 | if (dryrun) | ||
202 | continue; | ||
203 | |||
204 | /* We accept flattened tree phandles either in | ||
205 | * ePAPR-style "phandle" properties, or the | ||
206 | * legacy "linux,phandle" properties. If both | ||
207 | * appear and have different values, things | ||
208 | * will get weird. Don't do that. | ||
209 | */ | ||
210 | if (!strcmp(pname, "phandle") || | ||
211 | !strcmp(pname, "linux,phandle")) { | ||
212 | if (!np->phandle) | ||
213 | np->phandle = be32_to_cpup(val); | ||
214 | } | ||
215 | |||
216 | /* And we process the "ibm,phandle" property | ||
217 | * used in pSeries dynamic device tree | ||
218 | * stuff | ||
219 | */ | ||
220 | if (!strcmp(pname, "ibm,phandle")) | ||
221 | np->phandle = be32_to_cpup(val); | ||
222 | |||
223 | pp->name = (char *)pname; | ||
224 | pp->length = sz; | ||
225 | pp->value = (__be32 *)val; | ||
226 | *pprev = pp; | ||
227 | pprev = &pp->next; | ||
228 | } | ||
229 | |||
230 | /* With version 0x10 we may not have the name property, | ||
231 | * recreate it here from the unit name if absent | ||
232 | */ | ||
233 | if (!has_name) { | ||
234 | const char *p = nodename, *ps = p, *pa = NULL; | ||
235 | int len; | ||
236 | |||
237 | while (*p) { | ||
238 | if ((*p) == '@') | ||
239 | pa = p; | ||
240 | else if ((*p) == '/') | ||
241 | ps = p + 1; | ||
242 | p++; | ||
243 | } | ||
244 | |||
245 | if (pa < ps) | ||
246 | pa = p; | ||
247 | len = (pa - ps) + 1; | ||
248 | pp = unflatten_dt_alloc(mem, sizeof(struct property) + len, | ||
249 | __alignof__(struct property)); | ||
250 | if (!dryrun) { | ||
251 | pp->name = "name"; | ||
252 | pp->length = len; | ||
253 | pp->value = pp + 1; | ||
254 | *pprev = pp; | ||
255 | pprev = &pp->next; | ||
256 | memcpy(pp->value, ps, len - 1); | ||
257 | ((char *)pp->value)[len - 1] = 0; | ||
258 | pr_debug("fixed up name for %s -> %s\n", | ||
259 | nodename, (char *)pp->value); | ||
260 | } | ||
261 | } | ||
262 | |||
263 | if (!dryrun) | ||
264 | *pprev = NULL; | ||
265 | } | ||
266 | |||
267 | static unsigned long populate_node(const void *blob, | ||
268 | int offset, | ||
269 | void **mem, | ||
270 | struct device_node *dad, | ||
271 | unsigned long fpsize, | ||
272 | struct device_node **pnp, | ||
273 | bool dryrun) | ||
274 | { | ||
184 | struct device_node *np; | 275 | struct device_node *np; |
185 | struct property *pp, **prev_pp = NULL; | ||
186 | const char *pathp; | 276 | const char *pathp; |
187 | unsigned int l, allocl; | 277 | unsigned int l, allocl; |
188 | static int depth; | ||
189 | int old_depth; | ||
190 | int offset; | ||
191 | int has_name = 0; | ||
192 | int new_format = 0; | 278 | int new_format = 0; |
193 | 279 | ||
194 | pathp = fdt_get_name(blob, *poffset, &l); | 280 | pathp = fdt_get_name(blob, offset, &l); |
195 | if (!pathp) | 281 | if (!pathp) { |
196 | return mem; | 282 | *pnp = NULL; |
283 | return 0; | ||
284 | } | ||
197 | 285 | ||
198 | allocl = ++l; | 286 | allocl = ++l; |
199 | 287 | ||
@@ -223,7 +311,7 @@ static void * unflatten_dt_node(const void *blob, | |||
223 | } | 311 | } |
224 | } | 312 | } |
225 | 313 | ||
226 | np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl, | 314 | np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl, |
227 | __alignof__(struct device_node)); | 315 | __alignof__(struct device_node)); |
228 | if (!dryrun) { | 316 | if (!dryrun) { |
229 | char *fn; | 317 | char *fn; |
@@ -246,89 +334,15 @@ static void * unflatten_dt_node(const void *blob, | |||
246 | } | 334 | } |
247 | memcpy(fn, pathp, l); | 335 | memcpy(fn, pathp, l); |
248 | 336 | ||
249 | prev_pp = &np->properties; | ||
250 | if (dad != NULL) { | 337 | if (dad != NULL) { |
251 | np->parent = dad; | 338 | np->parent = dad; |
252 | np->sibling = dad->child; | 339 | np->sibling = dad->child; |
253 | dad->child = np; | 340 | dad->child = np; |
254 | } | 341 | } |
255 | } | 342 | } |
256 | /* process properties */ | ||
257 | for (offset = fdt_first_property_offset(blob, *poffset); | ||
258 | (offset >= 0); | ||
259 | (offset = fdt_next_property_offset(blob, offset))) { | ||
260 | const char *pname; | ||
261 | u32 sz; | ||
262 | 343 | ||
263 | if (!(p = fdt_getprop_by_offset(blob, offset, &pname, &sz))) { | 344 | populate_properties(blob, offset, mem, np, pathp, dryrun); |
264 | offset = -FDT_ERR_INTERNAL; | ||
265 | break; | ||
266 | } | ||
267 | |||
268 | if (pname == NULL) { | ||
269 | pr_info("Can't find property name in list !\n"); | ||
270 | break; | ||
271 | } | ||
272 | if (strcmp(pname, "name") == 0) | ||
273 | has_name = 1; | ||
274 | pp = unflatten_dt_alloc(&mem, sizeof(struct property), | ||
275 | __alignof__(struct property)); | ||
276 | if (!dryrun) { | ||
277 | /* We accept flattened tree phandles either in | ||
278 | * ePAPR-style "phandle" properties, or the | ||
279 | * legacy "linux,phandle" properties. If both | ||
280 | * appear and have different values, things | ||
281 | * will get weird. Don't do that. */ | ||
282 | if ((strcmp(pname, "phandle") == 0) || | ||
283 | (strcmp(pname, "linux,phandle") == 0)) { | ||
284 | if (np->phandle == 0) | ||
285 | np->phandle = be32_to_cpup(p); | ||
286 | } | ||
287 | /* And we process the "ibm,phandle" property | ||
288 | * used in pSeries dynamic device tree | ||
289 | * stuff */ | ||
290 | if (strcmp(pname, "ibm,phandle") == 0) | ||
291 | np->phandle = be32_to_cpup(p); | ||
292 | pp->name = (char *)pname; | ||
293 | pp->length = sz; | ||
294 | pp->value = (__be32 *)p; | ||
295 | *prev_pp = pp; | ||
296 | prev_pp = &pp->next; | ||
297 | } | ||
298 | } | ||
299 | /* with version 0x10 we may not have the name property, recreate | ||
300 | * it here from the unit name if absent | ||
301 | */ | ||
302 | if (!has_name) { | ||
303 | const char *p1 = pathp, *ps = pathp, *pa = NULL; | ||
304 | int sz; | ||
305 | |||
306 | while (*p1) { | ||
307 | if ((*p1) == '@') | ||
308 | pa = p1; | ||
309 | if ((*p1) == '/') | ||
310 | ps = p1 + 1; | ||
311 | p1++; | ||
312 | } | ||
313 | if (pa < ps) | ||
314 | pa = p1; | ||
315 | sz = (pa - ps) + 1; | ||
316 | pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz, | ||
317 | __alignof__(struct property)); | ||
318 | if (!dryrun) { | ||
319 | pp->name = "name"; | ||
320 | pp->length = sz; | ||
321 | pp->value = pp + 1; | ||
322 | *prev_pp = pp; | ||
323 | prev_pp = &pp->next; | ||
324 | memcpy(pp->value, ps, sz - 1); | ||
325 | ((char *)pp->value)[sz - 1] = 0; | ||
326 | pr_debug("fixed up name for %s -> %s\n", pathp, | ||
327 | (char *)pp->value); | ||
328 | } | ||
329 | } | ||
330 | if (!dryrun) { | 345 | if (!dryrun) { |
331 | *prev_pp = NULL; | ||
332 | np->name = of_get_property(np, "name", NULL); | 346 | np->name = of_get_property(np, "name", NULL); |
333 | np->type = of_get_property(np, "device_type", NULL); | 347 | np->type = of_get_property(np, "device_type", NULL); |
334 | 348 | ||
@@ -338,6 +352,37 @@ static void * unflatten_dt_node(const void *blob, | |||
338 | np->type = "<NULL>"; | 352 | np->type = "<NULL>"; |
339 | } | 353 | } |
340 | 354 | ||
355 | *pnp = np; | ||
356 | return fpsize; | ||
357 | } | ||
358 | |||
359 | /** | ||
360 | * unflatten_dt_node - Alloc and populate a device_node from the flat tree | ||
361 | * @blob: The parent device tree blob | ||
362 | * @mem: Memory chunk to use for allocating device nodes and properties | ||
363 | * @poffset: pointer to node in flat tree | ||
364 | * @dad: Parent struct device_node | ||
365 | * @nodepp: The device_node tree created by the call | ||
366 | * @fpsize: Size of the node path up at the current depth. | ||
367 | * @dryrun: If true, do not allocate device nodes but still calculate needed | ||
368 | * memory size | ||
369 | */ | ||
370 | static void *unflatten_dt_node(const void *blob, | ||
371 | void *mem, | ||
372 | int *poffset, | ||
373 | struct device_node *dad, | ||
374 | struct device_node **nodepp, | ||
375 | unsigned long fpsize, | ||
376 | bool dryrun) | ||
377 | { | ||
378 | struct device_node *np; | ||
379 | static int depth; | ||
380 | int old_depth; | ||
381 | |||
382 | fpsize = populate_node(blob, *poffset, &mem, dad, fpsize, &np, dryrun); | ||
383 | if (!fpsize) | ||
384 | return mem; | ||
385 | |||
341 | old_depth = depth; | 386 | old_depth = depth; |
342 | *poffset = fdt_next_node(blob, *poffset, &depth); | 387 | *poffset = fdt_next_node(blob, *poffset, &depth); |
343 | if (depth < 0) | 388 | if (depth < 0) |