]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
9e512045 SG |
2 | /* |
3 | * Copyright (c) 2017 Google, Inc | |
4 | * Written by Simon Glass <[email protected]> | |
9e512045 SG |
5 | */ |
6 | ||
92291652 SG |
7 | #define LOG_CATEGORY LOGC_DT |
8 | ||
9e512045 SG |
9 | #include <common.h> |
10 | #include <dm.h> | |
11 | #include <fdtdec.h> | |
12 | #include <fdt_support.h> | |
f7ae49fc | 13 | #include <log.h> |
336d4615 | 14 | #include <malloc.h> |
a8f2ac2a | 15 | #include <of_live.h> |
b08c8c48 | 16 | #include <linux/libfdt.h> |
9e512045 | 17 | #include <dm/of_access.h> |
bed77496 | 18 | #include <dm/of_addr.h> |
9e512045 SG |
19 | #include <dm/ofnode.h> |
20 | #include <linux/err.h> | |
dcf98852 | 21 | #include <linux/ioport.h> |
401d1c4f | 22 | #include <asm/global_data.h> |
9e512045 | 23 | |
92291652 SG |
24 | DECLARE_GLOBAL_DATA_PTR; |
25 | ||
26 | #if CONFIG_IS_ENABLED(OFNODE_MULTI_TREE) | |
27 | static void *oftree_list[CONFIG_OFNODE_MULTI_TREE_MAX]; | |
28 | static int oftree_count; | |
29 | ||
30 | void oftree_reset(void) | |
31 | { | |
32 | if (gd->flags & GD_FLG_RELOC) { | |
33 | oftree_count = 0; | |
34 | oftree_list[oftree_count++] = (void *)gd->fdt_blob; | |
35 | } | |
36 | } | |
37 | ||
38 | static int oftree_find(const void *fdt) | |
39 | { | |
40 | int i; | |
41 | ||
42 | for (i = 0; i < oftree_count; i++) { | |
43 | if (fdt == oftree_list[i]) | |
44 | return i; | |
45 | } | |
46 | ||
47 | return -1; | |
48 | } | |
49 | ||
50 | static oftree oftree_ensure(void *fdt) | |
51 | { | |
52 | oftree tree; | |
53 | int i; | |
54 | ||
a8f2ac2a SG |
55 | if (of_live_active()) { |
56 | struct device_node *root; | |
57 | int ret; | |
58 | ||
59 | ret = unflatten_device_tree(fdt, &root); | |
60 | if (ret) { | |
61 | log_err("Failed to create live tree: err=%d\n", ret); | |
62 | return oftree_null(); | |
63 | } | |
64 | tree = oftree_from_np(root); | |
65 | ||
66 | return tree; | |
67 | } | |
68 | ||
92291652 SG |
69 | if (gd->flags & GD_FLG_RELOC) { |
70 | i = oftree_find(fdt); | |
71 | if (i == -1) { | |
72 | if (oftree_count == CONFIG_OFNODE_MULTI_TREE_MAX) { | |
73 | log_warning("Too many registered device trees (max %d)\n", | |
74 | CONFIG_OFNODE_MULTI_TREE_MAX); | |
75 | return oftree_null(); | |
76 | } | |
77 | ||
78 | /* register the new tree */ | |
79 | i = oftree_count++; | |
80 | oftree_list[i] = fdt; | |
81 | log_debug("oftree: registered tree %d: %p\n", i, fdt); | |
82 | } | |
83 | } else { | |
84 | if (fdt != gd->fdt_blob) { | |
c8a4e293 | 85 | log_debug("Only the control FDT can be accessed before relocation\n"); |
92291652 SG |
86 | return oftree_null(); |
87 | } | |
88 | } | |
89 | ||
90 | tree.fdt = fdt; | |
91 | ||
92 | return tree; | |
93 | } | |
94 | ||
a8f2ac2a SG |
95 | void oftree_dispose(oftree tree) |
96 | { | |
97 | if (of_live_active()) | |
98 | of_live_free(tree.np); | |
99 | } | |
100 | ||
92291652 SG |
101 | void *ofnode_lookup_fdt(ofnode node) |
102 | { | |
103 | if (gd->flags & GD_FLG_RELOC) { | |
104 | uint i = OFTREE_TREE_ID(node.of_offset); | |
105 | ||
106 | if (i > oftree_count) { | |
107 | log_debug("Invalid tree ID %x\n", i); | |
108 | return NULL; | |
109 | } | |
110 | ||
111 | return oftree_list[i]; | |
112 | } else { | |
113 | return (void *)gd->fdt_blob; | |
114 | } | |
115 | } | |
116 | ||
117 | void *ofnode_to_fdt(ofnode node) | |
118 | { | |
119 | #ifdef OF_CHECKS | |
120 | if (of_live_active()) | |
121 | return NULL; | |
122 | #endif | |
123 | if (CONFIG_IS_ENABLED(OFNODE_MULTI_TREE) && ofnode_valid(node)) | |
124 | return ofnode_lookup_fdt(node); | |
125 | ||
126 | /* Use the control FDT by default */ | |
127 | return (void *)gd->fdt_blob; | |
128 | } | |
129 | ||
130 | /** | |
131 | * ofnode_to_offset() - convert an ofnode to a flat DT offset | |
132 | * | |
133 | * This cannot be called if the reference contains a node pointer. | |
134 | * | |
135 | * @node: Reference containing offset (possibly invalid) | |
136 | * Return: DT offset (can be -1) | |
137 | */ | |
138 | int ofnode_to_offset(ofnode node) | |
139 | { | |
140 | #ifdef OF_CHECKS | |
141 | if (of_live_active()) | |
142 | return -1; | |
143 | #endif | |
144 | if (CONFIG_IS_ENABLED(OFNODE_MULTI_TREE) && node.of_offset >= 0) | |
145 | return OFTREE_OFFSET(node.of_offset); | |
146 | ||
147 | return node.of_offset; | |
148 | } | |
149 | ||
150 | oftree oftree_from_fdt(void *fdt) | |
151 | { | |
152 | oftree tree; | |
153 | ||
154 | if (CONFIG_IS_ENABLED(OFNODE_MULTI_TREE)) | |
155 | return oftree_ensure(fdt); | |
156 | ||
a6c4f188 SG |
157 | #ifdef OF_CHECKS |
158 | if (of_live_active()) | |
159 | return oftree_null(); | |
160 | #endif | |
92291652 SG |
161 | tree.fdt = fdt; |
162 | ||
163 | return tree; | |
164 | } | |
165 | ||
166 | /** | |
167 | * noffset_to_ofnode() - convert a DT offset to an ofnode | |
168 | * | |
169 | * @other_node: Node in the same tree to use as a reference | |
170 | * @of_offset: DT offset (either valid, or -1) | |
171 | * Return: reference to the associated DT offset | |
172 | */ | |
173 | ofnode noffset_to_ofnode(ofnode other_node, int of_offset) | |
174 | { | |
175 | ofnode node; | |
176 | ||
177 | if (of_live_active()) | |
178 | node.np = NULL; | |
179 | else if (!CONFIG_IS_ENABLED(OFNODE_MULTI_TREE) || of_offset < 0 || | |
180 | !ofnode_valid(other_node)) | |
181 | node.of_offset = of_offset; | |
182 | else | |
183 | node.of_offset = OFTREE_MAKE_NODE(other_node.of_offset, | |
184 | of_offset); | |
185 | ||
186 | return node; | |
187 | } | |
188 | ||
189 | #else /* !OFNODE_MULTI_TREE */ | |
190 | ||
191 | static inline int oftree_find(const void *fdt) | |
192 | { | |
193 | return 0; | |
194 | } | |
195 | ||
196 | #endif /* OFNODE_MULTI_TREE */ | |
197 | ||
b7bd94f1 SG |
198 | /** |
199 | * ofnode_from_tree_offset() - get an ofnode from a tree offset (flat tree) | |
200 | * | |
92291652 SG |
201 | * Looks up the tree and returns an ofnode with the correct of_offset (i.e. |
202 | * containing the tree ID). | |
b7bd94f1 | 203 | * |
92291652 SG |
204 | * If @offset is < 0 then this returns an ofnode with that offset and no tree |
205 | * ID. | |
b7bd94f1 SG |
206 | * |
207 | * @tree: tree to check | |
208 | * @offset: offset within that tree (can be < 0) | |
92291652 | 209 | * @return node for that offset, with the correct ID |
b7bd94f1 SG |
210 | */ |
211 | static ofnode ofnode_from_tree_offset(oftree tree, int offset) | |
212 | { | |
213 | ofnode node; | |
214 | ||
92291652 SG |
215 | if (CONFIG_IS_ENABLED(OFNODE_MULTI_TREE) && offset >= 0) { |
216 | int tree_id = oftree_find(tree.fdt); | |
217 | ||
218 | if (tree_id == -1) | |
219 | return ofnode_null(); | |
220 | node.of_offset = OFTREE_NODE(tree_id, offset); | |
221 | } else { | |
222 | node.of_offset = offset; | |
223 | } | |
b7bd94f1 SG |
224 | |
225 | return node; | |
226 | } | |
227 | ||
77cbaf88 KVA |
228 | bool ofnode_name_eq(ofnode node, const char *name) |
229 | { | |
230 | const char *node_name; | |
231 | size_t len; | |
232 | ||
233 | assert(ofnode_valid(node)); | |
234 | ||
235 | node_name = ofnode_get_name(node); | |
236 | len = strchrnul(node_name, '@') - node_name; | |
237 | ||
238 | return (strlen(name) == len) && !strncmp(node_name, name, len); | |
239 | } | |
240 | ||
b471bdc4 SH |
241 | int ofnode_read_u8(ofnode node, const char *propname, u8 *outp) |
242 | { | |
243 | const u8 *cell; | |
244 | int len; | |
245 | ||
246 | assert(ofnode_valid(node)); | |
247 | debug("%s: %s: ", __func__, propname); | |
248 | ||
249 | if (ofnode_is_np(node)) | |
250 | return of_read_u8(ofnode_to_np(node), propname, outp); | |
251 | ||
252 | cell = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), propname, | |
253 | &len); | |
254 | if (!cell || len < sizeof(*cell)) { | |
255 | debug("(not found)\n"); | |
256 | return -EINVAL; | |
257 | } | |
258 | *outp = *cell; | |
259 | debug("%#x (%d)\n", *outp, *outp); | |
260 | ||
261 | return 0; | |
262 | } | |
263 | ||
264 | u8 ofnode_read_u8_default(ofnode node, const char *propname, u8 def) | |
265 | { | |
266 | assert(ofnode_valid(node)); | |
267 | ofnode_read_u8(node, propname, &def); | |
268 | ||
269 | return def; | |
270 | } | |
271 | ||
272 | int ofnode_read_u16(ofnode node, const char *propname, u16 *outp) | |
273 | { | |
274 | const fdt16_t *cell; | |
275 | int len; | |
276 | ||
277 | assert(ofnode_valid(node)); | |
278 | debug("%s: %s: ", __func__, propname); | |
279 | ||
280 | if (ofnode_is_np(node)) | |
281 | return of_read_u16(ofnode_to_np(node), propname, outp); | |
282 | ||
283 | cell = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), propname, | |
284 | &len); | |
285 | if (!cell || len < sizeof(*cell)) { | |
286 | debug("(not found)\n"); | |
287 | return -EINVAL; | |
288 | } | |
289 | *outp = be16_to_cpup(cell); | |
290 | debug("%#x (%d)\n", *outp, *outp); | |
291 | ||
292 | return 0; | |
293 | } | |
294 | ||
295 | u16 ofnode_read_u16_default(ofnode node, const char *propname, u16 def) | |
296 | { | |
297 | assert(ofnode_valid(node)); | |
298 | ofnode_read_u16(node, propname, &def); | |
299 | ||
300 | return def; | |
301 | } | |
302 | ||
9e512045 SG |
303 | int ofnode_read_u32(ofnode node, const char *propname, u32 *outp) |
304 | { | |
59006608 | 305 | return ofnode_read_u32_index(node, propname, 0, outp); |
9e512045 SG |
306 | } |
307 | ||
b061ef39 | 308 | u32 ofnode_read_u32_default(ofnode node, const char *propname, u32 def) |
9e512045 SG |
309 | { |
310 | assert(ofnode_valid(node)); | |
59006608 | 311 | ofnode_read_u32_index(node, propname, 0, &def); |
9e512045 SG |
312 | |
313 | return def; | |
314 | } | |
315 | ||
4bb7075c DB |
316 | int ofnode_read_u32_index(ofnode node, const char *propname, int index, |
317 | u32 *outp) | |
318 | { | |
319 | const fdt32_t *cell; | |
320 | int len; | |
321 | ||
322 | assert(ofnode_valid(node)); | |
323 | debug("%s: %s: ", __func__, propname); | |
324 | ||
325 | if (ofnode_is_np(node)) | |
326 | return of_read_u32_index(ofnode_to_np(node), propname, index, | |
327 | outp); | |
328 | ||
a3f50d03 SG |
329 | cell = fdt_getprop(ofnode_to_fdt(node), ofnode_to_offset(node), |
330 | propname, &len); | |
4bb7075c DB |
331 | if (!cell) { |
332 | debug("(not found)\n"); | |
333 | return -EINVAL; | |
334 | } | |
335 | ||
336 | if (len < (sizeof(int) * (index + 1))) { | |
337 | debug("(not large enough)\n"); | |
338 | return -EOVERFLOW; | |
339 | } | |
340 | ||
341 | *outp = fdt32_to_cpu(cell[index]); | |
342 | debug("%#x (%d)\n", *outp, *outp); | |
343 | ||
344 | return 0; | |
345 | } | |
346 | ||
347 | u32 ofnode_read_u32_index_default(ofnode node, const char *propname, int index, | |
348 | u32 def) | |
349 | { | |
350 | assert(ofnode_valid(node)); | |
351 | ofnode_read_u32_index(node, propname, index, &def); | |
352 | ||
353 | return def; | |
354 | } | |
355 | ||
9e512045 SG |
356 | int ofnode_read_s32_default(ofnode node, const char *propname, s32 def) |
357 | { | |
358 | assert(ofnode_valid(node)); | |
359 | ofnode_read_u32(node, propname, (u32 *)&def); | |
360 | ||
361 | return def; | |
362 | } | |
363 | ||
7e5196c4 SG |
364 | int ofnode_read_u64(ofnode node, const char *propname, u64 *outp) |
365 | { | |
d60ae4c5 | 366 | const unaligned_fdt64_t *cell; |
7e5196c4 SG |
367 | int len; |
368 | ||
369 | assert(ofnode_valid(node)); | |
370 | debug("%s: %s: ", __func__, propname); | |
371 | ||
372 | if (ofnode_is_np(node)) | |
373 | return of_read_u64(ofnode_to_np(node), propname, outp); | |
374 | ||
a3f50d03 SG |
375 | cell = fdt_getprop(ofnode_to_fdt(node), ofnode_to_offset(node), |
376 | propname, &len); | |
7e5196c4 SG |
377 | if (!cell || len < sizeof(*cell)) { |
378 | debug("(not found)\n"); | |
379 | return -EINVAL; | |
380 | } | |
381 | *outp = fdt64_to_cpu(cell[0]); | |
382 | debug("%#llx (%lld)\n", (unsigned long long)*outp, | |
383 | (unsigned long long)*outp); | |
384 | ||
385 | return 0; | |
386 | } | |
387 | ||
3f3d7715 | 388 | u64 ofnode_read_u64_default(ofnode node, const char *propname, u64 def) |
7e5196c4 SG |
389 | { |
390 | assert(ofnode_valid(node)); | |
391 | ofnode_read_u64(node, propname, &def); | |
392 | ||
393 | return def; | |
394 | } | |
395 | ||
9e512045 SG |
396 | bool ofnode_read_bool(ofnode node, const char *propname) |
397 | { | |
b2ec7ea7 | 398 | const void *prop; |
9e512045 SG |
399 | |
400 | assert(ofnode_valid(node)); | |
401 | debug("%s: %s: ", __func__, propname); | |
402 | ||
b2ec7ea7 MY |
403 | prop = ofnode_get_property(node, propname, NULL); |
404 | ||
405 | debug("%s\n", prop ? "true" : "false"); | |
9e512045 | 406 | |
b2ec7ea7 | 407 | return prop ? true : false; |
9e512045 SG |
408 | } |
409 | ||
a8167d8e | 410 | const void *ofnode_read_prop(ofnode node, const char *propname, int *sizep) |
9e512045 | 411 | { |
a8167d8e SG |
412 | const char *val = NULL; |
413 | int len; | |
9e512045 SG |
414 | |
415 | assert(ofnode_valid(node)); | |
416 | debug("%s: %s: ", __func__, propname); | |
417 | ||
418 | if (ofnode_is_np(node)) { | |
419 | struct property *prop = of_find_property( | |
a8167d8e | 420 | ofnode_to_np(node), propname, &len); |
9e512045 SG |
421 | |
422 | if (prop) { | |
a8167d8e | 423 | val = prop->value; |
9e512045 SG |
424 | len = prop->length; |
425 | } | |
426 | } else { | |
a3f50d03 | 427 | val = fdt_getprop(ofnode_to_fdt(node), ofnode_to_offset(node), |
9e512045 SG |
428 | propname, &len); |
429 | } | |
a8167d8e | 430 | if (!val) { |
9e512045 | 431 | debug("<not found>\n"); |
a8167d8e SG |
432 | if (sizep) |
433 | *sizep = -FDT_ERR_NOTFOUND; | |
9e512045 SG |
434 | return NULL; |
435 | } | |
a8167d8e SG |
436 | if (sizep) |
437 | *sizep = len; | |
438 | ||
439 | return val; | |
440 | } | |
441 | ||
442 | const char *ofnode_read_string(ofnode node, const char *propname) | |
443 | { | |
444 | const char *str; | |
445 | int len; | |
446 | ||
447 | str = ofnode_read_prop(node, propname, &len); | |
448 | if (!str) | |
449 | return NULL; | |
450 | ||
9e512045 SG |
451 | if (strnlen(str, len) >= len) { |
452 | debug("<invalid>\n"); | |
453 | return NULL; | |
454 | } | |
455 | debug("%s\n", str); | |
456 | ||
457 | return str; | |
458 | } | |
459 | ||
1aada631 SG |
460 | int ofnode_read_size(ofnode node, const char *propname) |
461 | { | |
462 | int len; | |
463 | ||
464 | if (!ofnode_read_prop(node, propname, &len)) | |
465 | return -EINVAL; | |
466 | ||
467 | return len; | |
468 | } | |
469 | ||
9e512045 SG |
470 | ofnode ofnode_find_subnode(ofnode node, const char *subnode_name) |
471 | { | |
472 | ofnode subnode; | |
473 | ||
474 | assert(ofnode_valid(node)); | |
475 | debug("%s: %s: ", __func__, subnode_name); | |
476 | ||
477 | if (ofnode_is_np(node)) { | |
98306987 | 478 | struct device_node *np = ofnode_to_np(node); |
9e512045 SG |
479 | |
480 | for (np = np->child; np; np = np->sibling) { | |
481 | if (!strcmp(subnode_name, np->name)) | |
482 | break; | |
483 | } | |
484 | subnode = np_to_ofnode(np); | |
485 | } else { | |
a3f50d03 | 486 | int ooffset = fdt_subnode_offset(ofnode_to_fdt(node), |
9e512045 | 487 | ofnode_to_offset(node), subnode_name); |
2187cb7e | 488 | subnode = noffset_to_ofnode(node, ooffset); |
9e512045 SG |
489 | } |
490 | debug("%s\n", ofnode_valid(subnode) ? | |
491 | ofnode_get_name(subnode) : "<none>"); | |
492 | ||
493 | return subnode; | |
494 | } | |
495 | ||
496 | int ofnode_read_u32_array(ofnode node, const char *propname, | |
497 | u32 *out_values, size_t sz) | |
498 | { | |
499 | assert(ofnode_valid(node)); | |
500 | debug("%s: %s: ", __func__, propname); | |
501 | ||
502 | if (ofnode_is_np(node)) { | |
503 | return of_read_u32_array(ofnode_to_np(node), propname, | |
504 | out_values, sz); | |
505 | } else { | |
66d0d0c1 SG |
506 | int ret; |
507 | ||
a3f50d03 | 508 | ret = fdtdec_get_int_array(ofnode_to_fdt(node), |
66d0d0c1 SG |
509 | ofnode_to_offset(node), propname, |
510 | out_values, sz); | |
511 | ||
512 | /* get the error right, but space is more important in SPL */ | |
513 | if (!IS_ENABLED(CONFIG_SPL_BUILD)) { | |
514 | if (ret == -FDT_ERR_NOTFOUND) | |
515 | return -EINVAL; | |
516 | else if (ret == -FDT_ERR_BADLAYOUT) | |
517 | return -EOVERFLOW; | |
518 | } | |
519 | return ret; | |
9e512045 SG |
520 | } |
521 | } | |
522 | ||
ec1add1e | 523 | #if !CONFIG_IS_ENABLED(DM_INLINE_OFNODE) |
0de1b074 SG |
524 | bool ofnode_is_enabled(ofnode node) |
525 | { | |
526 | if (ofnode_is_np(node)) { | |
527 | return of_device_is_available(ofnode_to_np(node)); | |
528 | } else { | |
a3f50d03 | 529 | return fdtdec_get_is_enabled(ofnode_to_fdt(node), |
0de1b074 SG |
530 | ofnode_to_offset(node)); |
531 | } | |
532 | } | |
533 | ||
9e512045 SG |
534 | ofnode ofnode_first_subnode(ofnode node) |
535 | { | |
536 | assert(ofnode_valid(node)); | |
537 | if (ofnode_is_np(node)) | |
538 | return np_to_ofnode(node.np->child); | |
539 | ||
2187cb7e | 540 | return noffset_to_ofnode(node, |
a3f50d03 | 541 | fdt_first_subnode(ofnode_to_fdt(node), ofnode_to_offset(node))); |
9e512045 SG |
542 | } |
543 | ||
544 | ofnode ofnode_next_subnode(ofnode node) | |
545 | { | |
546 | assert(ofnode_valid(node)); | |
547 | if (ofnode_is_np(node)) | |
548 | return np_to_ofnode(node.np->sibling); | |
549 | ||
2187cb7e | 550 | return noffset_to_ofnode(node, |
a3f50d03 | 551 | fdt_next_subnode(ofnode_to_fdt(node), ofnode_to_offset(node))); |
9e512045 | 552 | } |
ec1add1e | 553 | #endif /* !DM_INLINE_OFNODE */ |
9e512045 | 554 | |
e2d5997f PT |
555 | ofnode ofnode_get_parent(ofnode node) |
556 | { | |
557 | ofnode parent; | |
558 | ||
559 | assert(ofnode_valid(node)); | |
560 | if (ofnode_is_np(node)) | |
561 | parent = np_to_ofnode(of_get_parent(ofnode_to_np(node))); | |
562 | else | |
a3f50d03 | 563 | parent.of_offset = fdt_parent_offset(ofnode_to_fdt(node), |
e2d5997f PT |
564 | ofnode_to_offset(node)); |
565 | ||
566 | return parent; | |
567 | } | |
568 | ||
9e512045 SG |
569 | const char *ofnode_get_name(ofnode node) |
570 | { | |
8f0a70e8 KY |
571 | if (!ofnode_valid(node)) { |
572 | debug("%s node not valid\n", __func__); | |
573 | return NULL; | |
574 | } | |
575 | ||
9e512045 | 576 | if (ofnode_is_np(node)) |
f46ec93e | 577 | return node.np->name; |
9e512045 | 578 | |
a3f50d03 | 579 | return fdt_get_name(ofnode_to_fdt(node), ofnode_to_offset(node), NULL); |
9e512045 SG |
580 | } |
581 | ||
0e116bea MB |
582 | int ofnode_get_path(ofnode node, char *buf, int buflen) |
583 | { | |
584 | assert(ofnode_valid(node)); | |
585 | ||
586 | if (ofnode_is_np(node)) { | |
587 | if (strlen(node.np->full_name) >= buflen) | |
588 | return -ENOSPC; | |
589 | ||
590 | strcpy(buf, node.np->full_name); | |
591 | ||
592 | return 0; | |
593 | } else { | |
594 | int res; | |
595 | ||
a3f50d03 | 596 | res = fdt_get_path(ofnode_to_fdt(node), ofnode_to_offset(node), buf, |
0e116bea MB |
597 | buflen); |
598 | if (!res) | |
599 | return res; | |
600 | else if (res == -FDT_ERR_NOSPACE) | |
601 | return -ENOSPC; | |
602 | else | |
603 | return -EINVAL; | |
604 | } | |
605 | } | |
606 | ||
b4f20767 KY |
607 | ofnode ofnode_get_by_phandle(uint phandle) |
608 | { | |
609 | ofnode node; | |
610 | ||
611 | if (of_live_active()) | |
829d5124 | 612 | node = np_to_ofnode(of_find_node_by_phandle(NULL, phandle)); |
b4f20767 KY |
613 | else |
614 | node.of_offset = fdt_node_offset_by_phandle(gd->fdt_blob, | |
615 | phandle); | |
616 | ||
617 | return node; | |
618 | } | |
619 | ||
928d267a SG |
620 | ofnode oftree_get_by_phandle(oftree tree, uint phandle) |
621 | { | |
622 | ofnode node; | |
623 | ||
624 | if (of_live_active()) | |
625 | node = np_to_ofnode(of_find_node_by_phandle(tree.np, phandle)); | |
626 | else | |
5e96925b | 627 | node = ofnode_from_tree_offset(tree, |
928d267a | 628 | fdt_node_offset_by_phandle(oftree_lookup_fdt(tree), |
5e96925b | 629 | phandle)); |
928d267a SG |
630 | |
631 | return node; | |
632 | } | |
633 | ||
31a7b719 MB |
634 | static fdt_addr_t __ofnode_get_addr_size_index(ofnode node, int index, |
635 | fdt_size_t *size, bool translate) | |
bed77496 | 636 | { |
1678754f | 637 | int na, ns; |
1678754f | 638 | |
1b098b3e CG |
639 | if (size) |
640 | *size = FDT_SIZE_T_NONE; | |
aa351a14 | 641 | |
bed77496 SG |
642 | if (ofnode_is_np(node)) { |
643 | const __be32 *prop_val; | |
e18c41fc | 644 | u64 size64; |
bed77496 | 645 | uint flags; |
bed77496 | 646 | |
e18c41fc SG |
647 | prop_val = of_get_address(ofnode_to_np(node), index, &size64, |
648 | &flags); | |
bed77496 SG |
649 | if (!prop_val) |
650 | return FDT_ADDR_T_NONE; | |
1b098b3e | 651 | |
e18c41fc SG |
652 | if (size) |
653 | *size = size64; | |
286ede65 | 654 | |
e8d52918 MS |
655 | ns = of_n_size_cells(ofnode_to_np(node)); |
656 | ||
31a7b719 | 657 | if (translate && IS_ENABLED(CONFIG_OF_TRANSLATE) && ns > 0) { |
286ede65 MS |
658 | return of_translate_address(ofnode_to_np(node), prop_val); |
659 | } else { | |
660 | na = of_n_addr_cells(ofnode_to_np(node)); | |
661 | return of_read_number(prop_val, na); | |
662 | } | |
bed77496 | 663 | } else { |
1678754f K |
664 | na = ofnode_read_simple_addr_cells(ofnode_get_parent(node)); |
665 | ns = ofnode_read_simple_size_cells(ofnode_get_parent(node)); | |
a3f50d03 | 666 | return fdtdec_get_addr_size_fixed(ofnode_to_fdt(node), |
1678754f | 667 | ofnode_to_offset(node), "reg", |
31a7b719 MB |
668 | index, na, ns, size, |
669 | translate); | |
bed77496 | 670 | } |
bed77496 SG |
671 | } |
672 | ||
31a7b719 MB |
673 | fdt_addr_t ofnode_get_addr_size_index(ofnode node, int index, fdt_size_t *size) |
674 | { | |
675 | return __ofnode_get_addr_size_index(node, index, size, true); | |
676 | } | |
677 | ||
678 | fdt_addr_t ofnode_get_addr_size_index_notrans(ofnode node, int index, | |
679 | fdt_size_t *size) | |
680 | { | |
681 | return __ofnode_get_addr_size_index(node, index, size, false); | |
682 | } | |
683 | ||
e679d03b K |
684 | fdt_addr_t ofnode_get_addr_index(ofnode node, int index) |
685 | { | |
686 | fdt_size_t size; | |
687 | ||
688 | return ofnode_get_addr_size_index(node, index, &size); | |
689 | } | |
690 | ||
bed77496 SG |
691 | fdt_addr_t ofnode_get_addr(ofnode node) |
692 | { | |
693 | return ofnode_get_addr_index(node, 0); | |
694 | } | |
695 | ||
aa351a14 CG |
696 | fdt_size_t ofnode_get_size(ofnode node) |
697 | { | |
698 | fdt_size_t size; | |
699 | ||
700 | ofnode_get_addr_size_index(node, 0, &size); | |
701 | ||
702 | return size; | |
703 | } | |
704 | ||
9e512045 SG |
705 | int ofnode_stringlist_search(ofnode node, const char *property, |
706 | const char *string) | |
707 | { | |
708 | if (ofnode_is_np(node)) { | |
709 | return of_property_match_string(ofnode_to_np(node), | |
710 | property, string); | |
711 | } else { | |
712 | int ret; | |
713 | ||
a3f50d03 | 714 | ret = fdt_stringlist_search(ofnode_to_fdt(node), |
9e512045 SG |
715 | ofnode_to_offset(node), property, |
716 | string); | |
717 | if (ret == -FDT_ERR_NOTFOUND) | |
718 | return -ENODATA; | |
719 | else if (ret < 0) | |
720 | return -EINVAL; | |
721 | ||
722 | return ret; | |
723 | } | |
724 | } | |
725 | ||
726 | int ofnode_read_string_index(ofnode node, const char *property, int index, | |
727 | const char **outp) | |
728 | { | |
729 | if (ofnode_is_np(node)) { | |
730 | return of_property_read_string_index(ofnode_to_np(node), | |
731 | property, index, outp); | |
732 | } else { | |
733 | int len; | |
734 | ||
a3f50d03 SG |
735 | *outp = fdt_stringlist_get(ofnode_to_fdt(node), |
736 | ofnode_to_offset(node), | |
9e512045 SG |
737 | property, index, &len); |
738 | if (len < 0) | |
739 | return -EINVAL; | |
740 | return 0; | |
741 | } | |
742 | } | |
743 | ||
8c293d6a SG |
744 | int ofnode_read_string_count(ofnode node, const char *property) |
745 | { | |
746 | if (ofnode_is_np(node)) { | |
747 | return of_property_count_strings(ofnode_to_np(node), property); | |
748 | } else { | |
a3f50d03 | 749 | return fdt_stringlist_count(ofnode_to_fdt(node), |
8c293d6a SG |
750 | ofnode_to_offset(node), property); |
751 | } | |
752 | } | |
753 | ||
075bfc95 SG |
754 | int ofnode_read_string_list(ofnode node, const char *property, |
755 | const char ***listp) | |
756 | { | |
757 | const char **prop; | |
758 | int count; | |
759 | int i; | |
760 | ||
761 | *listp = NULL; | |
762 | count = ofnode_read_string_count(node, property); | |
763 | if (count < 0) | |
764 | return count; | |
765 | if (!count) | |
766 | return 0; | |
767 | ||
768 | prop = calloc(count + 1, sizeof(char *)); | |
769 | if (!prop) | |
770 | return -ENOMEM; | |
771 | ||
772 | for (i = 0; i < count; i++) | |
773 | ofnode_read_string_index(node, property, i, &prop[i]); | |
774 | prop[count] = NULL; | |
775 | *listp = prop; | |
776 | ||
777 | return count; | |
778 | } | |
779 | ||
9e512045 SG |
780 | static void ofnode_from_fdtdec_phandle_args(struct fdtdec_phandle_args *in, |
781 | struct ofnode_phandle_args *out) | |
782 | { | |
783 | assert(OF_MAX_PHANDLE_ARGS == MAX_PHANDLE_ARGS); | |
784 | out->node = offset_to_ofnode(in->node); | |
785 | out->args_count = in->args_count; | |
786 | memcpy(out->args, in->args, sizeof(out->args)); | |
787 | } | |
788 | ||
789 | static void ofnode_from_of_phandle_args(struct of_phandle_args *in, | |
790 | struct ofnode_phandle_args *out) | |
791 | { | |
792 | assert(OF_MAX_PHANDLE_ARGS == MAX_PHANDLE_ARGS); | |
793 | out->node = np_to_ofnode(in->np); | |
794 | out->args_count = in->args_count; | |
795 | memcpy(out->args, in->args, sizeof(out->args)); | |
796 | } | |
797 | ||
798 | int ofnode_parse_phandle_with_args(ofnode node, const char *list_name, | |
799 | const char *cells_name, int cell_count, | |
800 | int index, | |
801 | struct ofnode_phandle_args *out_args) | |
802 | { | |
803 | if (ofnode_is_np(node)) { | |
804 | struct of_phandle_args args; | |
805 | int ret; | |
806 | ||
807 | ret = of_parse_phandle_with_args(ofnode_to_np(node), | |
01d89e3d PD |
808 | list_name, cells_name, |
809 | cell_count, index, | |
51db287a | 810 | &args); |
9e512045 SG |
811 | if (ret) |
812 | return ret; | |
813 | ofnode_from_of_phandle_args(&args, out_args); | |
814 | } else { | |
815 | struct fdtdec_phandle_args args; | |
816 | int ret; | |
817 | ||
a3f50d03 | 818 | ret = fdtdec_parse_phandle_with_args(ofnode_to_fdt(node), |
51db287a MS |
819 | ofnode_to_offset(node), |
820 | list_name, cells_name, | |
821 | cell_count, index, &args); | |
9e512045 SG |
822 | if (ret) |
823 | return ret; | |
824 | ofnode_from_fdtdec_phandle_args(&args, out_args); | |
825 | } | |
826 | ||
827 | return 0; | |
828 | } | |
829 | ||
642346ae | 830 | int ofnode_count_phandle_with_args(ofnode node, const char *list_name, |
89f68302 | 831 | const char *cells_name, int cell_count) |
642346ae PC |
832 | { |
833 | if (ofnode_is_np(node)) | |
834 | return of_count_phandle_with_args(ofnode_to_np(node), | |
89f68302 | 835 | list_name, cells_name, cell_count); |
642346ae | 836 | else |
a3f50d03 | 837 | return fdtdec_parse_phandle_with_args(ofnode_to_fdt(node), |
642346ae | 838 | ofnode_to_offset(node), list_name, cells_name, |
89f68302 | 839 | cell_count, -1, NULL); |
642346ae PC |
840 | } |
841 | ||
9e512045 SG |
842 | ofnode ofnode_path(const char *path) |
843 | { | |
844 | if (of_live_active()) | |
845 | return np_to_ofnode(of_find_node_by_path(path)); | |
846 | else | |
847 | return offset_to_ofnode(fdt_path_offset(gd->fdt_blob, path)); | |
848 | } | |
849 | ||
b7bd94f1 | 850 | ofnode oftree_root(oftree tree) |
33104847 | 851 | { |
b7bd94f1 SG |
852 | if (of_live_active()) { |
853 | return np_to_ofnode(tree.np); | |
854 | } else { | |
855 | return ofnode_from_tree_offset(tree, 0); | |
856 | } | |
857 | } | |
858 | ||
859 | ofnode oftree_path(oftree tree, const char *path) | |
860 | { | |
861 | if (of_live_active()) { | |
33104847 SG |
862 | return np_to_ofnode(of_find_node_opts_by_path(tree.np, path, |
863 | NULL)); | |
b7bd94f1 | 864 | } else if (*path != '/' && tree.fdt != gd->fdt_blob) { |
33104847 | 865 | return ofnode_null(); /* Aliases only on control FDT */ |
b7bd94f1 SG |
866 | } else { |
867 | int offset = fdt_path_offset(tree.fdt, path); | |
868 | ||
869 | return ofnode_from_tree_offset(tree, offset); | |
870 | } | |
33104847 SG |
871 | } |
872 | ||
bd933bfd | 873 | const void *ofnode_read_chosen_prop(const char *propname, int *sizep) |
9e512045 SG |
874 | { |
875 | ofnode chosen_node; | |
876 | ||
877 | chosen_node = ofnode_path("/chosen"); | |
878 | ||
bd933bfd SG |
879 | return ofnode_read_prop(chosen_node, propname, sizep); |
880 | } | |
881 | ||
882 | const char *ofnode_read_chosen_string(const char *propname) | |
883 | { | |
884 | return ofnode_read_chosen_prop(propname, NULL); | |
9e512045 SG |
885 | } |
886 | ||
887 | ofnode ofnode_get_chosen_node(const char *name) | |
888 | { | |
889 | const char *prop; | |
890 | ||
bd933bfd | 891 | prop = ofnode_read_chosen_prop(name, NULL); |
9e512045 SG |
892 | if (!prop) |
893 | return ofnode_null(); | |
894 | ||
895 | return ofnode_path(prop); | |
896 | } | |
897 | ||
305d3188 MS |
898 | const void *ofnode_read_aliases_prop(const char *propname, int *sizep) |
899 | { | |
900 | ofnode node; | |
901 | ||
902 | node = ofnode_path("/aliases"); | |
903 | ||
904 | return ofnode_read_prop(node, propname, sizep); | |
905 | } | |
906 | ||
907 | ofnode ofnode_get_aliases_node(const char *name) | |
908 | { | |
909 | const char *prop; | |
910 | ||
911 | prop = ofnode_read_aliases_prop(name, NULL); | |
912 | if (!prop) | |
913 | return ofnode_null(); | |
914 | ||
915 | debug("%s: node_path: %s\n", __func__, prop); | |
916 | ||
917 | return ofnode_path(prop); | |
918 | } | |
919 | ||
89b84b85 CY |
920 | int ofnode_get_child_count(ofnode parent) |
921 | { | |
922 | ofnode child; | |
923 | int num = 0; | |
924 | ||
925 | ofnode_for_each_subnode(child, parent) | |
926 | num++; | |
927 | ||
928 | return num; | |
929 | } | |
930 | ||
9e512045 SG |
931 | static int decode_timing_property(ofnode node, const char *name, |
932 | struct timing_entry *result) | |
933 | { | |
934 | int length, ret = 0; | |
935 | ||
936 | length = ofnode_read_size(node, name); | |
937 | if (length < 0) { | |
938 | debug("%s: could not find property %s\n", | |
939 | ofnode_get_name(node), name); | |
940 | return length; | |
941 | } | |
942 | ||
943 | if (length == sizeof(u32)) { | |
944 | result->typ = ofnode_read_u32_default(node, name, 0); | |
945 | result->min = result->typ; | |
946 | result->max = result->typ; | |
947 | } else { | |
948 | ret = ofnode_read_u32_array(node, name, &result->min, 3); | |
949 | } | |
950 | ||
951 | return ret; | |
952 | } | |
953 | ||
954 | int ofnode_decode_display_timing(ofnode parent, int index, | |
955 | struct display_timing *dt) | |
956 | { | |
957 | int i; | |
958 | ofnode timings, node; | |
959 | u32 val = 0; | |
960 | int ret = 0; | |
961 | ||
962 | timings = ofnode_find_subnode(parent, "display-timings"); | |
963 | if (!ofnode_valid(timings)) | |
964 | return -EINVAL; | |
965 | ||
3991f42e SG |
966 | i = 0; |
967 | ofnode_for_each_subnode(node, timings) { | |
968 | if (i++ == index) | |
969 | break; | |
970 | } | |
9e512045 SG |
971 | |
972 | if (!ofnode_valid(node)) | |
973 | return -EINVAL; | |
974 | ||
975 | memset(dt, 0, sizeof(*dt)); | |
976 | ||
977 | ret |= decode_timing_property(node, "hback-porch", &dt->hback_porch); | |
978 | ret |= decode_timing_property(node, "hfront-porch", &dt->hfront_porch); | |
979 | ret |= decode_timing_property(node, "hactive", &dt->hactive); | |
980 | ret |= decode_timing_property(node, "hsync-len", &dt->hsync_len); | |
981 | ret |= decode_timing_property(node, "vback-porch", &dt->vback_porch); | |
982 | ret |= decode_timing_property(node, "vfront-porch", &dt->vfront_porch); | |
983 | ret |= decode_timing_property(node, "vactive", &dt->vactive); | |
984 | ret |= decode_timing_property(node, "vsync-len", &dt->vsync_len); | |
985 | ret |= decode_timing_property(node, "clock-frequency", &dt->pixelclock); | |
986 | ||
987 | dt->flags = 0; | |
988 | val = ofnode_read_u32_default(node, "vsync-active", -1); | |
989 | if (val != -1) { | |
990 | dt->flags |= val ? DISPLAY_FLAGS_VSYNC_HIGH : | |
991 | DISPLAY_FLAGS_VSYNC_LOW; | |
992 | } | |
993 | val = ofnode_read_u32_default(node, "hsync-active", -1); | |
994 | if (val != -1) { | |
995 | dt->flags |= val ? DISPLAY_FLAGS_HSYNC_HIGH : | |
996 | DISPLAY_FLAGS_HSYNC_LOW; | |
997 | } | |
998 | val = ofnode_read_u32_default(node, "de-active", -1); | |
999 | if (val != -1) { | |
1000 | dt->flags |= val ? DISPLAY_FLAGS_DE_HIGH : | |
1001 | DISPLAY_FLAGS_DE_LOW; | |
1002 | } | |
1003 | val = ofnode_read_u32_default(node, "pixelclk-active", -1); | |
1004 | if (val != -1) { | |
1005 | dt->flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE : | |
1006 | DISPLAY_FLAGS_PIXDATA_NEGEDGE; | |
1007 | } | |
1008 | ||
1009 | if (ofnode_read_bool(node, "interlaced")) | |
1010 | dt->flags |= DISPLAY_FLAGS_INTERLACED; | |
1011 | if (ofnode_read_bool(node, "doublescan")) | |
1012 | dt->flags |= DISPLAY_FLAGS_DOUBLESCAN; | |
1013 | if (ofnode_read_bool(node, "doubleclk")) | |
1014 | dt->flags |= DISPLAY_FLAGS_DOUBLECLK; | |
1015 | ||
1016 | return ret; | |
1017 | } | |
1018 | ||
0347cc77 NJ |
1019 | int ofnode_decode_panel_timing(ofnode parent, |
1020 | struct display_timing *dt) | |
1021 | { | |
1022 | ofnode timings; | |
1023 | u32 val = 0; | |
1024 | int ret = 0; | |
1025 | ||
cd88058d | 1026 | timings = ofnode_find_subnode(parent, "panel-timing"); |
0347cc77 NJ |
1027 | if (!ofnode_valid(timings)) |
1028 | return -EINVAL; | |
1029 | memset(dt, 0, sizeof(*dt)); | |
1030 | ret |= decode_timing_property(timings, "hback-porch", &dt->hback_porch); | |
1031 | ret |= decode_timing_property(timings, "hfront-porch", &dt->hfront_porch); | |
1032 | ret |= decode_timing_property(timings, "hactive", &dt->hactive); | |
1033 | ret |= decode_timing_property(timings, "hsync-len", &dt->hsync_len); | |
1034 | ret |= decode_timing_property(timings, "vback-porch", &dt->vback_porch); | |
1035 | ret |= decode_timing_property(timings, "vfront-porch", &dt->vfront_porch); | |
1036 | ret |= decode_timing_property(timings, "vactive", &dt->vactive); | |
1037 | ret |= decode_timing_property(timings, "vsync-len", &dt->vsync_len); | |
1038 | ret |= decode_timing_property(timings, "clock-frequency", &dt->pixelclock); | |
1039 | dt->flags = 0; | |
1040 | if (!ofnode_read_u32(timings, "vsync-active", &val)) { | |
1041 | dt->flags |= val ? DISPLAY_FLAGS_VSYNC_HIGH : | |
1042 | DISPLAY_FLAGS_VSYNC_LOW; | |
1043 | } | |
1044 | if (!ofnode_read_u32(timings, "hsync-active", &val)) { | |
1045 | dt->flags |= val ? DISPLAY_FLAGS_HSYNC_HIGH : | |
1046 | DISPLAY_FLAGS_HSYNC_LOW; | |
1047 | } | |
1048 | if (!ofnode_read_u32(timings, "de-active", &val)) { | |
1049 | dt->flags |= val ? DISPLAY_FLAGS_DE_HIGH : | |
1050 | DISPLAY_FLAGS_DE_LOW; | |
1051 | } | |
1052 | if (!ofnode_read_u32(timings, "pixelclk-active", &val)) { | |
1053 | dt->flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE : | |
1054 | DISPLAY_FLAGS_PIXDATA_NEGEDGE; | |
1055 | } | |
1056 | if (ofnode_read_bool(timings, "interlaced")) | |
1057 | dt->flags |= DISPLAY_FLAGS_INTERLACED; | |
1058 | if (ofnode_read_bool(timings, "doublescan")) | |
1059 | dt->flags |= DISPLAY_FLAGS_DOUBLESCAN; | |
1060 | if (ofnode_read_bool(timings, "doubleclk")) | |
1061 | dt->flags |= DISPLAY_FLAGS_DOUBLECLK; | |
1062 | ||
1063 | return ret; | |
1064 | } | |
1065 | ||
61e51bab | 1066 | const void *ofnode_get_property(ofnode node, const char *propname, int *lenp) |
9e512045 | 1067 | { |
cb7dbe1f MY |
1068 | if (ofnode_is_np(node)) |
1069 | return of_get_property(ofnode_to_np(node), propname, lenp); | |
1070 | else | |
a3f50d03 | 1071 | return fdt_getprop(ofnode_to_fdt(node), ofnode_to_offset(node), |
9e512045 | 1072 | propname, lenp); |
9e512045 SG |
1073 | } |
1074 | ||
4b1f5714 | 1075 | int ofnode_first_property(ofnode node, struct ofprop *prop) |
ce891fca PD |
1076 | { |
1077 | prop->node = node; | |
1078 | ||
1079 | if (ofnode_is_np(node)) { | |
1080 | prop->prop = of_get_first_property(ofnode_to_np(prop->node)); | |
1081 | if (!prop->prop) | |
1082 | return -FDT_ERR_NOTFOUND; | |
1083 | } else { | |
1084 | prop->offset = | |
a3f50d03 | 1085 | fdt_first_property_offset(ofnode_to_fdt(node), |
ce891fca PD |
1086 | ofnode_to_offset(prop->node)); |
1087 | if (prop->offset < 0) | |
1088 | return prop->offset; | |
1089 | } | |
1090 | ||
1091 | return 0; | |
1092 | } | |
1093 | ||
4b1f5714 | 1094 | int ofnode_next_property(struct ofprop *prop) |
ce891fca PD |
1095 | { |
1096 | if (ofnode_is_np(prop->node)) { | |
1097 | prop->prop = of_get_next_property(ofnode_to_np(prop->node), | |
1098 | prop->prop); | |
1099 | if (!prop->prop) | |
1100 | return -FDT_ERR_NOTFOUND; | |
1101 | } else { | |
a3f50d03 SG |
1102 | prop->offset = |
1103 | fdt_next_property_offset(ofnode_to_fdt(prop->node), | |
1104 | prop->offset); | |
ce891fca PD |
1105 | if (prop->offset < 0) |
1106 | return prop->offset; | |
1107 | } | |
1108 | ||
1109 | return 0; | |
1110 | } | |
1111 | ||
92432246 SG |
1112 | const void *ofprop_get_property(const struct ofprop *prop, |
1113 | const char **propname, int *lenp) | |
ce891fca PD |
1114 | { |
1115 | if (ofnode_is_np(prop->node)) | |
1116 | return of_get_property_by_prop(ofnode_to_np(prop->node), | |
1117 | prop->prop, propname, lenp); | |
1118 | else | |
a3f50d03 | 1119 | return fdt_getprop_by_offset(ofnode_to_fdt(prop->node), |
ce891fca PD |
1120 | prop->offset, |
1121 | propname, lenp); | |
1122 | } | |
1123 | ||
9e512045 SG |
1124 | fdt_addr_t ofnode_get_addr_size(ofnode node, const char *property, |
1125 | fdt_size_t *sizep) | |
1126 | { | |
1127 | if (ofnode_is_np(node)) { | |
1128 | int na, ns; | |
1129 | int psize; | |
1130 | const struct device_node *np = ofnode_to_np(node); | |
68a34529 | 1131 | const __be32 *prop = of_get_property(np, property, &psize); |
9e512045 | 1132 | |
68a34529 KG |
1133 | if (!prop) |
1134 | return FDT_ADDR_T_NONE; | |
9e512045 | 1135 | na = of_n_addr_cells(np); |
51cb9275 | 1136 | ns = of_n_size_cells(np); |
a4b8e372 | 1137 | *sizep = of_read_number(prop + na, ns); |
a6a45cd3 | 1138 | |
a47abd7b | 1139 | if (CONFIG_IS_ENABLED(OF_TRANSLATE) && ns > 0) |
a6a45cd3 MV |
1140 | return of_translate_address(np, prop); |
1141 | else | |
1142 | return of_read_number(prop, na); | |
9e512045 | 1143 | } else { |
a3f50d03 | 1144 | return fdtdec_get_addr_size(ofnode_to_fdt(node), |
9e512045 SG |
1145 | ofnode_to_offset(node), property, |
1146 | sizep); | |
1147 | } | |
1148 | } | |
1149 | ||
1150 | const uint8_t *ofnode_read_u8_array_ptr(ofnode node, const char *propname, | |
1151 | size_t sz) | |
1152 | { | |
1153 | if (ofnode_is_np(node)) { | |
1154 | const struct device_node *np = ofnode_to_np(node); | |
1155 | int psize; | |
1156 | const __be32 *prop = of_get_property(np, propname, &psize); | |
1157 | ||
1158 | if (!prop || sz != psize) | |
1159 | return NULL; | |
1160 | return (uint8_t *)prop; | |
1161 | ||
1162 | } else { | |
a3f50d03 | 1163 | return fdtdec_locate_byte_array(ofnode_to_fdt(node), |
9e512045 SG |
1164 | ofnode_to_offset(node), propname, sz); |
1165 | } | |
1166 | } | |
1167 | ||
1168 | int ofnode_read_pci_addr(ofnode node, enum fdt_pci_space type, | |
1169 | const char *propname, struct fdt_pci_addr *addr) | |
1170 | { | |
8c9eaada | 1171 | const fdt32_t *cell; |
9e512045 SG |
1172 | int len; |
1173 | int ret = -ENOENT; | |
1174 | ||
1175 | debug("%s: %s: ", __func__, propname); | |
1176 | ||
1177 | /* | |
1178 | * If we follow the pci bus bindings strictly, we should check | |
1179 | * the value of the node's parent node's #address-cells and | |
1180 | * #size-cells. They need to be 3 and 2 accordingly. However, | |
1181 | * for simplicity we skip the check here. | |
1182 | */ | |
61e51bab | 1183 | cell = ofnode_get_property(node, propname, &len); |
9e512045 SG |
1184 | if (!cell) |
1185 | goto fail; | |
1186 | ||
1187 | if ((len % FDT_PCI_REG_SIZE) == 0) { | |
1188 | int num = len / FDT_PCI_REG_SIZE; | |
1189 | int i; | |
1190 | ||
1191 | for (i = 0; i < num; i++) { | |
1192 | debug("pci address #%d: %08lx %08lx %08lx\n", i, | |
1193 | (ulong)fdt32_to_cpu(cell[0]), | |
1194 | (ulong)fdt32_to_cpu(cell[1]), | |
1195 | (ulong)fdt32_to_cpu(cell[2])); | |
1196 | if ((fdt32_to_cpu(*cell) & type) == type) { | |
1197 | addr->phys_hi = fdt32_to_cpu(cell[0]); | |
1198 | addr->phys_mid = fdt32_to_cpu(cell[1]); | |
e587886a | 1199 | addr->phys_lo = fdt32_to_cpu(cell[2]); |
9e512045 | 1200 | break; |
9e512045 | 1201 | } |
51db287a MS |
1202 | |
1203 | cell += (FDT_PCI_ADDR_CELLS + | |
1204 | FDT_PCI_SIZE_CELLS); | |
9e512045 SG |
1205 | } |
1206 | ||
1207 | if (i == num) { | |
1208 | ret = -ENXIO; | |
1209 | goto fail; | |
1210 | } | |
1211 | ||
1212 | return 0; | |
9e512045 SG |
1213 | } |
1214 | ||
51db287a MS |
1215 | ret = -EINVAL; |
1216 | ||
9e512045 SG |
1217 | fail: |
1218 | debug("(not found)\n"); | |
1219 | return ret; | |
1220 | } | |
1221 | ||
7b9cbadd BM |
1222 | int ofnode_read_pci_vendev(ofnode node, u16 *vendor, u16 *device) |
1223 | { | |
1224 | const char *list, *end; | |
1225 | int len; | |
1226 | ||
1227 | list = ofnode_get_property(node, "compatible", &len); | |
1228 | if (!list) | |
1229 | return -ENOENT; | |
1230 | ||
1231 | end = list + len; | |
1232 | while (list < end) { | |
1233 | len = strlen(list); | |
1234 | if (len >= strlen("pciVVVV,DDDD")) { | |
1235 | char *s = strstr(list, "pci"); | |
1236 | ||
1237 | /* | |
1238 | * check if the string is something like pciVVVV,DDDD.RR | |
1239 | * or just pciVVVV,DDDD | |
1240 | */ | |
1241 | if (s && s[7] == ',' && | |
1242 | (s[12] == '.' || s[12] == 0)) { | |
1243 | s += 3; | |
1244 | *vendor = simple_strtol(s, NULL, 16); | |
1245 | ||
1246 | s += 5; | |
1247 | *device = simple_strtol(s, NULL, 16); | |
1248 | ||
1249 | return 0; | |
1250 | } | |
1251 | } | |
1252 | list += (len + 1); | |
db681d49 MS |
1253 | } |
1254 | ||
1255 | return -ENOENT; | |
1256 | } | |
1257 | ||
1258 | int ofnode_read_eth_phy_id(ofnode node, u16 *vendor, u16 *device) | |
1259 | { | |
1260 | const char *list, *end; | |
1261 | int len; | |
1262 | ||
1263 | list = ofnode_get_property(node, "compatible", &len); | |
1264 | ||
1265 | if (!list) | |
1266 | return -ENOENT; | |
1267 | ||
1268 | end = list + len; | |
1269 | while (list < end) { | |
1270 | len = strlen(list); | |
1271 | ||
2e9946ab | 1272 | if (len >= strlen("ethernet-phy-idVVVV.DDDD")) { |
db681d49 MS |
1273 | char *s = strstr(list, "ethernet-phy-id"); |
1274 | ||
1275 | /* | |
1276 | * check if the string is something like | |
2e9946ab | 1277 | * ethernet-phy-idVVVV.DDDD |
db681d49 MS |
1278 | */ |
1279 | if (s && s[19] == '.') { | |
1280 | s += strlen("ethernet-phy-id"); | |
1281 | *vendor = simple_strtol(s, NULL, 16); | |
1282 | s += 5; | |
1283 | *device = simple_strtol(s, NULL, 16); | |
1284 | ||
1285 | return 0; | |
1286 | } | |
1287 | } | |
1288 | list += (len + 1); | |
7b9cbadd BM |
1289 | } |
1290 | ||
1291 | return -ENOENT; | |
1292 | } | |
1293 | ||
9e512045 SG |
1294 | int ofnode_read_addr_cells(ofnode node) |
1295 | { | |
ae6b33dc | 1296 | if (ofnode_is_np(node)) { |
9e512045 | 1297 | return of_n_addr_cells(ofnode_to_np(node)); |
ae6b33dc | 1298 | } else { |
a3f50d03 | 1299 | int parent = fdt_parent_offset(ofnode_to_fdt(node), |
ae6b33dc HS |
1300 | ofnode_to_offset(node)); |
1301 | ||
a3f50d03 | 1302 | return fdt_address_cells(ofnode_to_fdt(node), parent); |
ae6b33dc | 1303 | } |
9e512045 SG |
1304 | } |
1305 | ||
1306 | int ofnode_read_size_cells(ofnode node) | |
1307 | { | |
ae6b33dc | 1308 | if (ofnode_is_np(node)) { |
9e512045 | 1309 | return of_n_size_cells(ofnode_to_np(node)); |
ae6b33dc | 1310 | } else { |
a3f50d03 | 1311 | int parent = fdt_parent_offset(ofnode_to_fdt(node), |
ae6b33dc HS |
1312 | ofnode_to_offset(node)); |
1313 | ||
a3f50d03 | 1314 | return fdt_size_cells(ofnode_to_fdt(node), parent); |
ae6b33dc | 1315 | } |
878d68c0 SG |
1316 | } |
1317 | ||
1318 | int ofnode_read_simple_addr_cells(ofnode node) | |
1319 | { | |
1320 | if (ofnode_is_np(node)) | |
1321 | return of_simple_addr_cells(ofnode_to_np(node)); | |
1322 | else | |
a3f50d03 SG |
1323 | return fdt_address_cells(ofnode_to_fdt(node), |
1324 | ofnode_to_offset(node)); | |
878d68c0 SG |
1325 | } |
1326 | ||
1327 | int ofnode_read_simple_size_cells(ofnode node) | |
1328 | { | |
1329 | if (ofnode_is_np(node)) | |
1330 | return of_simple_size_cells(ofnode_to_np(node)); | |
9e512045 | 1331 | else |
a3f50d03 SG |
1332 | return fdt_size_cells(ofnode_to_fdt(node), |
1333 | ofnode_to_offset(node)); | |
9e512045 SG |
1334 | } |
1335 | ||
1336 | bool ofnode_pre_reloc(ofnode node) | |
1337 | { | |
c7a88dae PD |
1338 | #if defined(CONFIG_SPL_BUILD) || defined(CONFIG_TPL_BUILD) |
1339 | /* for SPL and TPL the remaining nodes after the fdtgrep 1st pass | |
e316fbab | 1340 | * had property bootph-all or bootph-pre-sram/bootph-pre-ram. |
c7a88dae PD |
1341 | * They are removed in final dtb (fdtgrep 2nd pass) |
1342 | */ | |
1343 | return true; | |
1344 | #else | |
e316fbab | 1345 | if (ofnode_read_bool(node, "bootph-all")) |
9e512045 | 1346 | return true; |
e316fbab | 1347 | if (ofnode_read_bool(node, "bootph-some-ram")) |
06f94461 | 1348 | return true; |
9e512045 | 1349 | |
9e512045 SG |
1350 | /* |
1351 | * In regular builds individual spl and tpl handling both | |
1352 | * count as handled pre-relocation for later second init. | |
1353 | */ | |
e316fbab SG |
1354 | if (ofnode_read_bool(node, "bootph-pre-ram") || |
1355 | ofnode_read_bool(node, "bootph-pre-sram")) | |
9e512045 | 1356 | return true; |
9e512045 | 1357 | |
c74e0341 SG |
1358 | if (IS_ENABLED(CONFIG_OF_TAG_MIGRATE)) { |
1359 | /* detect and handle old tags */ | |
1360 | if (ofnode_read_bool(node, "u-boot,dm-pre-reloc") || | |
1361 | ofnode_read_bool(node, "u-boot,dm-pre-proper") || | |
1362 | ofnode_read_bool(node, "u-boot,dm-spl") || | |
1363 | ofnode_read_bool(node, "u-boot,dm-tpl") || | |
1364 | ofnode_read_bool(node, "u-boot,dm-vpl")) { | |
1365 | gd->flags |= GD_FLG_OF_TAG_MIGRATE; | |
1366 | return true; | |
1367 | } | |
1368 | } | |
1369 | ||
9e512045 | 1370 | return false; |
c7a88dae | 1371 | #endif |
9e512045 | 1372 | } |
dcf98852 SG |
1373 | |
1374 | int ofnode_read_resource(ofnode node, uint index, struct resource *res) | |
1375 | { | |
1376 | if (ofnode_is_np(node)) { | |
1377 | return of_address_to_resource(ofnode_to_np(node), index, res); | |
1378 | } else { | |
1379 | struct fdt_resource fres; | |
1380 | int ret; | |
1381 | ||
a3f50d03 SG |
1382 | ret = fdt_get_resource(ofnode_to_fdt(node), |
1383 | ofnode_to_offset(node), | |
dcf98852 SG |
1384 | "reg", index, &fres); |
1385 | if (ret < 0) | |
1386 | return -EINVAL; | |
1387 | memset(res, '\0', sizeof(*res)); | |
1388 | res->start = fres.start; | |
1389 | res->end = fres.end; | |
1390 | ||
1391 | return 0; | |
1392 | } | |
1393 | } | |
7b8b47bd MY |
1394 | |
1395 | int ofnode_read_resource_byname(ofnode node, const char *name, | |
1396 | struct resource *res) | |
1397 | { | |
1398 | int index; | |
1399 | ||
1400 | index = ofnode_stringlist_search(node, "reg-names", name); | |
1401 | if (index < 0) | |
1402 | return index; | |
1403 | ||
1404 | return ofnode_read_resource(node, index, res); | |
1405 | } | |
147c6074 MS |
1406 | |
1407 | u64 ofnode_translate_address(ofnode node, const fdt32_t *in_addr) | |
1408 | { | |
1409 | if (ofnode_is_np(node)) | |
1410 | return of_translate_address(ofnode_to_np(node), in_addr); | |
1411 | else | |
a3f50d03 SG |
1412 | return fdt_translate_address(ofnode_to_fdt(node), |
1413 | ofnode_to_offset(node), in_addr); | |
147c6074 | 1414 | } |
5ccc2c21 | 1415 | |
641067fb FD |
1416 | u64 ofnode_translate_dma_address(ofnode node, const fdt32_t *in_addr) |
1417 | { | |
1418 | if (ofnode_is_np(node)) | |
1419 | return of_translate_dma_address(ofnode_to_np(node), in_addr); | |
1420 | else | |
a3f50d03 SG |
1421 | return fdt_translate_dma_address(ofnode_to_fdt(node), |
1422 | ofnode_to_offset(node), in_addr); | |
641067fb FD |
1423 | } |
1424 | ||
51bdb509 NSJ |
1425 | int ofnode_get_dma_range(ofnode node, phys_addr_t *cpu, dma_addr_t *bus, u64 *size) |
1426 | { | |
1427 | if (ofnode_is_np(node)) | |
1428 | return of_get_dma_range(ofnode_to_np(node), cpu, bus, size); | |
1429 | else | |
a3f50d03 SG |
1430 | return fdt_get_dma_range(ofnode_to_fdt(node), |
1431 | ofnode_to_offset(node), | |
51bdb509 NSJ |
1432 | cpu, bus, size); |
1433 | } | |
1434 | ||
5ccc2c21 MY |
1435 | int ofnode_device_is_compatible(ofnode node, const char *compat) |
1436 | { | |
1437 | if (ofnode_is_np(node)) | |
1438 | return of_device_is_compatible(ofnode_to_np(node), compat, | |
1439 | NULL, NULL); | |
1440 | else | |
a3f50d03 | 1441 | return !fdt_node_check_compatible(ofnode_to_fdt(node), |
5ccc2c21 MY |
1442 | ofnode_to_offset(node), |
1443 | compat); | |
1444 | } | |
c60f671b SG |
1445 | |
1446 | ofnode ofnode_by_compatible(ofnode from, const char *compat) | |
1447 | { | |
1448 | if (of_live_active()) { | |
1449 | return np_to_ofnode(of_find_compatible_node( | |
1450 | (struct device_node *)ofnode_to_np(from), NULL, | |
1451 | compat)); | |
1452 | } else { | |
2187cb7e SG |
1453 | return noffset_to_ofnode(from, |
1454 | fdt_node_offset_by_compatible(ofnode_to_fdt(from), | |
a3f50d03 | 1455 | ofnode_to_offset(from), compat)); |
c60f671b SG |
1456 | } |
1457 | } | |
61fba0fa JW |
1458 | |
1459 | ofnode ofnode_by_prop_value(ofnode from, const char *propname, | |
1460 | const void *propval, int proplen) | |
1461 | { | |
1462 | if (of_live_active()) { | |
1463 | return np_to_ofnode(of_find_node_by_prop_value( | |
1464 | (struct device_node *)ofnode_to_np(from), propname, | |
1465 | propval, proplen)); | |
1466 | } else { | |
2187cb7e SG |
1467 | return noffset_to_ofnode(from, |
1468 | fdt_node_offset_by_prop_value(ofnode_to_fdt(from), | |
1469 | ofnode_to_offset(from), propname, propval, | |
1470 | proplen)); | |
61fba0fa JW |
1471 | } |
1472 | } | |
e369e58d | 1473 | |
be0789a8 | 1474 | int ofnode_write_prop(ofnode node, const char *propname, const void *value, |
0b58eaa8 | 1475 | int len, bool copy) |
e369e58d | 1476 | { |
0b58eaa8 SG |
1477 | if (of_live_active()) { |
1478 | void *newval; | |
1479 | int ret; | |
1480 | ||
1481 | if (copy) { | |
1482 | newval = malloc(len); | |
1483 | if (!newval) | |
1484 | return log_ret(-ENOMEM); | |
1485 | memcpy(newval, value, len); | |
1486 | value = newval; | |
1487 | } | |
1488 | ret = of_write_prop(ofnode_to_np(node), propname, len, value); | |
1489 | if (ret && copy) | |
1490 | free(newval); | |
1491 | return ret; | |
1492 | } else { | |
a3f50d03 | 1493 | return fdt_setprop(ofnode_to_fdt(node), ofnode_to_offset(node), |
39e42be1 | 1494 | propname, value, len); |
0b58eaa8 | 1495 | } |
e369e58d MS |
1496 | } |
1497 | ||
1498 | int ofnode_write_string(ofnode node, const char *propname, const char *value) | |
1499 | { | |
e369e58d MS |
1500 | assert(ofnode_valid(node)); |
1501 | ||
1502 | debug("%s: %s = %s", __func__, propname, value); | |
1503 | ||
0b58eaa8 SG |
1504 | return ofnode_write_prop(node, propname, value, strlen(value) + 1, |
1505 | false); | |
e369e58d MS |
1506 | } |
1507 | ||
55f7990b SG |
1508 | int ofnode_write_u32(ofnode node, const char *propname, u32 value) |
1509 | { | |
1510 | fdt32_t *val; | |
1511 | ||
1512 | assert(ofnode_valid(node)); | |
1513 | ||
1514 | log_debug("%s = %x", propname, value); | |
1515 | val = malloc(sizeof(*val)); | |
1516 | if (!val) | |
1517 | return -ENOMEM; | |
1518 | *val = cpu_to_fdt32(value); | |
1519 | ||
0b58eaa8 | 1520 | return ofnode_write_prop(node, propname, val, sizeof(value), false); |
55f7990b SG |
1521 | } |
1522 | ||
e369e58d MS |
1523 | int ofnode_set_enabled(ofnode node, bool value) |
1524 | { | |
e369e58d MS |
1525 | assert(ofnode_valid(node)); |
1526 | ||
1527 | if (value) | |
1528 | return ofnode_write_string(node, "status", "okay"); | |
1529 | else | |
16351212 | 1530 | return ofnode_write_string(node, "status", "disabled"); |
e369e58d | 1531 | } |
7de8bd03 SG |
1532 | |
1533 | bool ofnode_conf_read_bool(const char *prop_name) | |
1534 | { | |
1535 | ofnode node; | |
1536 | ||
1537 | node = ofnode_path("/config"); | |
1538 | if (!ofnode_valid(node)) | |
1539 | return false; | |
1540 | ||
1541 | return ofnode_read_bool(node, prop_name); | |
1542 | } | |
1543 | ||
1544 | int ofnode_conf_read_int(const char *prop_name, int default_val) | |
1545 | { | |
1546 | ofnode node; | |
1547 | ||
1548 | node = ofnode_path("/config"); | |
1549 | if (!ofnode_valid(node)) | |
1550 | return default_val; | |
1551 | ||
1552 | return ofnode_read_u32_default(node, prop_name, default_val); | |
1553 | } | |
1554 | ||
1555 | const char *ofnode_conf_read_str(const char *prop_name) | |
1556 | { | |
1557 | ofnode node; | |
1558 | ||
1559 | node = ofnode_path("/config"); | |
1560 | if (!ofnode_valid(node)) | |
1561 | return NULL; | |
1562 | ||
1563 | return ofnode_read_string(node, prop_name); | |
1564 | } | |
f3dd213e MB |
1565 | |
1566 | ofnode ofnode_get_phy_node(ofnode node) | |
1567 | { | |
1568 | /* DT node properties that reference a PHY node */ | |
1569 | static const char * const phy_handle_str[] = { | |
1570 | "phy-handle", "phy", "phy-device", | |
1571 | }; | |
1572 | struct ofnode_phandle_args args = { | |
1573 | .node = ofnode_null() | |
1574 | }; | |
1575 | int i; | |
1576 | ||
1577 | assert(ofnode_valid(node)); | |
1578 | ||
1579 | for (i = 0; i < ARRAY_SIZE(phy_handle_str); i++) | |
1580 | if (!ofnode_parse_phandle_with_args(node, phy_handle_str[i], | |
1581 | NULL, 0, 0, &args)) | |
1582 | break; | |
1583 | ||
1584 | return args.node; | |
1585 | } | |
123ca114 MB |
1586 | |
1587 | phy_interface_t ofnode_read_phy_mode(ofnode node) | |
1588 | { | |
1589 | const char *mode; | |
1590 | int i; | |
1591 | ||
1592 | assert(ofnode_valid(node)); | |
1593 | ||
1594 | mode = ofnode_read_string(node, "phy-mode"); | |
1595 | if (!mode) | |
1596 | mode = ofnode_read_string(node, "phy-connection-type"); | |
1597 | ||
1598 | if (!mode) | |
ffb0f6f4 | 1599 | return PHY_INTERFACE_MODE_NA; |
123ca114 | 1600 | |
6706d7dc | 1601 | for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) |
123ca114 MB |
1602 | if (!strcmp(mode, phy_interface_strings[i])) |
1603 | return i; | |
1604 | ||
1605 | debug("%s: Invalid PHY interface '%s'\n", __func__, mode); | |
1606 | ||
ffb0f6f4 | 1607 | return PHY_INTERFACE_MODE_NA; |
123ca114 | 1608 | } |
ffe90392 SG |
1609 | |
1610 | int ofnode_add_subnode(ofnode node, const char *name, ofnode *subnodep) | |
1611 | { | |
1612 | ofnode subnode; | |
1613 | int ret = 0; | |
1614 | ||
1615 | assert(ofnode_valid(node)); | |
1616 | ||
1617 | if (ofnode_is_np(node)) { | |
1618 | struct device_node *np, *child; | |
1619 | ||
1620 | np = (struct device_node *)ofnode_to_np(node); | |
1621 | ret = of_add_subnode(np, name, -1, &child); | |
1622 | if (ret && ret != -EEXIST) | |
1623 | return ret; | |
1624 | subnode = np_to_ofnode(child); | |
1625 | } else { | |
a3f50d03 | 1626 | void *fdt = ofnode_to_fdt(node); |
ffe90392 SG |
1627 | int poffset = ofnode_to_offset(node); |
1628 | int offset; | |
1629 | ||
1630 | offset = fdt_add_subnode(fdt, poffset, name); | |
1631 | if (offset == -FDT_ERR_EXISTS) { | |
1632 | offset = fdt_subnode_offset(fdt, poffset, name); | |
1633 | ret = -EEXIST; | |
1634 | } | |
1635 | if (offset < 0) | |
1636 | return -EINVAL; | |
2187cb7e | 1637 | subnode = noffset_to_ofnode(node, offset); |
ffe90392 SG |
1638 | } |
1639 | ||
1640 | *subnodep = subnode; | |
1641 | ||
1642 | return ret; /* 0 or -EEXIST */ | |
1643 | } | |
db1ef1e1 SG |
1644 | |
1645 | int ofnode_copy_props(ofnode src, ofnode dst) | |
1646 | { | |
1647 | struct ofprop prop; | |
1648 | ||
1649 | ofnode_for_each_prop(prop, src) { | |
1650 | const char *name; | |
1651 | const char *val; | |
1652 | int len, ret; | |
1653 | ||
1654 | val = ofprop_get_property(&prop, &name, &len); | |
1655 | if (!val) { | |
1656 | log_debug("Cannot read prop (err=%d)\n", len); | |
1657 | return log_msg_ret("get", -EINVAL); | |
1658 | } | |
1659 | ret = ofnode_write_prop(dst, name, val, len, true); | |
1660 | if (ret) { | |
1661 | log_debug("Cannot write prop (err=%d)\n", ret); | |
1662 | return log_msg_ret("wr", -EINVAL); | |
1663 | } | |
1664 | } | |
1665 | ||
1666 | return 0; | |
1667 | } |