]>
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 | ||
7 | #include <common.h> | |
8 | #include <dm.h> | |
9 | #include <fdtdec.h> | |
10 | #include <fdt_support.h> | |
f7ae49fc | 11 | #include <log.h> |
336d4615 | 12 | #include <malloc.h> |
b08c8c48 | 13 | #include <linux/libfdt.h> |
9e512045 | 14 | #include <dm/of_access.h> |
bed77496 | 15 | #include <dm/of_addr.h> |
9e512045 SG |
16 | #include <dm/ofnode.h> |
17 | #include <linux/err.h> | |
dcf98852 | 18 | #include <linux/ioport.h> |
9e512045 SG |
19 | |
20 | int ofnode_read_u32(ofnode node, const char *propname, u32 *outp) | |
21 | { | |
59006608 | 22 | return ofnode_read_u32_index(node, propname, 0, outp); |
9e512045 SG |
23 | } |
24 | ||
b061ef39 | 25 | u32 ofnode_read_u32_default(ofnode node, const char *propname, u32 def) |
9e512045 SG |
26 | { |
27 | assert(ofnode_valid(node)); | |
59006608 | 28 | ofnode_read_u32_index(node, propname, 0, &def); |
9e512045 SG |
29 | |
30 | return def; | |
31 | } | |
32 | ||
4bb7075c DB |
33 | int ofnode_read_u32_index(ofnode node, const char *propname, int index, |
34 | u32 *outp) | |
35 | { | |
36 | const fdt32_t *cell; | |
37 | int len; | |
38 | ||
39 | assert(ofnode_valid(node)); | |
40 | debug("%s: %s: ", __func__, propname); | |
41 | ||
42 | if (ofnode_is_np(node)) | |
43 | return of_read_u32_index(ofnode_to_np(node), propname, index, | |
44 | outp); | |
45 | ||
46 | cell = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), propname, | |
47 | &len); | |
48 | if (!cell) { | |
49 | debug("(not found)\n"); | |
50 | return -EINVAL; | |
51 | } | |
52 | ||
53 | if (len < (sizeof(int) * (index + 1))) { | |
54 | debug("(not large enough)\n"); | |
55 | return -EOVERFLOW; | |
56 | } | |
57 | ||
58 | *outp = fdt32_to_cpu(cell[index]); | |
59 | debug("%#x (%d)\n", *outp, *outp); | |
60 | ||
61 | return 0; | |
62 | } | |
63 | ||
64 | u32 ofnode_read_u32_index_default(ofnode node, const char *propname, int index, | |
65 | u32 def) | |
66 | { | |
67 | assert(ofnode_valid(node)); | |
68 | ofnode_read_u32_index(node, propname, index, &def); | |
69 | ||
70 | return def; | |
71 | } | |
72 | ||
9e512045 SG |
73 | int ofnode_read_s32_default(ofnode node, const char *propname, s32 def) |
74 | { | |
75 | assert(ofnode_valid(node)); | |
76 | ofnode_read_u32(node, propname, (u32 *)&def); | |
77 | ||
78 | return def; | |
79 | } | |
80 | ||
7e5196c4 SG |
81 | int ofnode_read_u64(ofnode node, const char *propname, u64 *outp) |
82 | { | |
d60ae4c5 | 83 | const unaligned_fdt64_t *cell; |
7e5196c4 SG |
84 | int len; |
85 | ||
86 | assert(ofnode_valid(node)); | |
87 | debug("%s: %s: ", __func__, propname); | |
88 | ||
89 | if (ofnode_is_np(node)) | |
90 | return of_read_u64(ofnode_to_np(node), propname, outp); | |
91 | ||
92 | cell = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), propname, | |
93 | &len); | |
94 | if (!cell || len < sizeof(*cell)) { | |
95 | debug("(not found)\n"); | |
96 | return -EINVAL; | |
97 | } | |
98 | *outp = fdt64_to_cpu(cell[0]); | |
99 | debug("%#llx (%lld)\n", (unsigned long long)*outp, | |
100 | (unsigned long long)*outp); | |
101 | ||
102 | return 0; | |
103 | } | |
104 | ||
3f3d7715 | 105 | u64 ofnode_read_u64_default(ofnode node, const char *propname, u64 def) |
7e5196c4 SG |
106 | { |
107 | assert(ofnode_valid(node)); | |
108 | ofnode_read_u64(node, propname, &def); | |
109 | ||
110 | return def; | |
111 | } | |
112 | ||
9e512045 SG |
113 | bool ofnode_read_bool(ofnode node, const char *propname) |
114 | { | |
b2ec7ea7 | 115 | const void *prop; |
9e512045 SG |
116 | |
117 | assert(ofnode_valid(node)); | |
118 | debug("%s: %s: ", __func__, propname); | |
119 | ||
b2ec7ea7 MY |
120 | prop = ofnode_get_property(node, propname, NULL); |
121 | ||
122 | debug("%s\n", prop ? "true" : "false"); | |
9e512045 | 123 | |
b2ec7ea7 | 124 | return prop ? true : false; |
9e512045 SG |
125 | } |
126 | ||
a8167d8e | 127 | const void *ofnode_read_prop(ofnode node, const char *propname, int *sizep) |
9e512045 | 128 | { |
a8167d8e SG |
129 | const char *val = NULL; |
130 | int len; | |
9e512045 SG |
131 | |
132 | assert(ofnode_valid(node)); | |
133 | debug("%s: %s: ", __func__, propname); | |
134 | ||
135 | if (ofnode_is_np(node)) { | |
136 | struct property *prop = of_find_property( | |
a8167d8e | 137 | ofnode_to_np(node), propname, &len); |
9e512045 SG |
138 | |
139 | if (prop) { | |
a8167d8e | 140 | val = prop->value; |
9e512045 SG |
141 | len = prop->length; |
142 | } | |
143 | } else { | |
a8167d8e | 144 | val = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), |
9e512045 SG |
145 | propname, &len); |
146 | } | |
a8167d8e | 147 | if (!val) { |
9e512045 | 148 | debug("<not found>\n"); |
a8167d8e SG |
149 | if (sizep) |
150 | *sizep = -FDT_ERR_NOTFOUND; | |
9e512045 SG |
151 | return NULL; |
152 | } | |
a8167d8e SG |
153 | if (sizep) |
154 | *sizep = len; | |
155 | ||
156 | return val; | |
157 | } | |
158 | ||
159 | const char *ofnode_read_string(ofnode node, const char *propname) | |
160 | { | |
161 | const char *str; | |
162 | int len; | |
163 | ||
164 | str = ofnode_read_prop(node, propname, &len); | |
165 | if (!str) | |
166 | return NULL; | |
167 | ||
9e512045 SG |
168 | if (strnlen(str, len) >= len) { |
169 | debug("<invalid>\n"); | |
170 | return NULL; | |
171 | } | |
172 | debug("%s\n", str); | |
173 | ||
174 | return str; | |
175 | } | |
176 | ||
1aada631 SG |
177 | int ofnode_read_size(ofnode node, const char *propname) |
178 | { | |
179 | int len; | |
180 | ||
181 | if (!ofnode_read_prop(node, propname, &len)) | |
182 | return -EINVAL; | |
183 | ||
184 | return len; | |
185 | } | |
186 | ||
9e512045 SG |
187 | ofnode ofnode_find_subnode(ofnode node, const char *subnode_name) |
188 | { | |
189 | ofnode subnode; | |
190 | ||
191 | assert(ofnode_valid(node)); | |
192 | debug("%s: %s: ", __func__, subnode_name); | |
193 | ||
194 | if (ofnode_is_np(node)) { | |
195 | const struct device_node *np = ofnode_to_np(node); | |
196 | ||
197 | for (np = np->child; np; np = np->sibling) { | |
198 | if (!strcmp(subnode_name, np->name)) | |
199 | break; | |
200 | } | |
201 | subnode = np_to_ofnode(np); | |
202 | } else { | |
203 | int ooffset = fdt_subnode_offset(gd->fdt_blob, | |
204 | ofnode_to_offset(node), subnode_name); | |
205 | subnode = offset_to_ofnode(ooffset); | |
206 | } | |
207 | debug("%s\n", ofnode_valid(subnode) ? | |
208 | ofnode_get_name(subnode) : "<none>"); | |
209 | ||
210 | return subnode; | |
211 | } | |
212 | ||
213 | int ofnode_read_u32_array(ofnode node, const char *propname, | |
214 | u32 *out_values, size_t sz) | |
215 | { | |
216 | assert(ofnode_valid(node)); | |
217 | debug("%s: %s: ", __func__, propname); | |
218 | ||
219 | if (ofnode_is_np(node)) { | |
220 | return of_read_u32_array(ofnode_to_np(node), propname, | |
221 | out_values, sz); | |
222 | } else { | |
223 | return fdtdec_get_int_array(gd->fdt_blob, | |
224 | ofnode_to_offset(node), propname, | |
225 | out_values, sz); | |
226 | } | |
227 | } | |
228 | ||
0de1b074 SG |
229 | bool ofnode_is_enabled(ofnode node) |
230 | { | |
231 | if (ofnode_is_np(node)) { | |
232 | return of_device_is_available(ofnode_to_np(node)); | |
233 | } else { | |
234 | return fdtdec_get_is_enabled(gd->fdt_blob, | |
235 | ofnode_to_offset(node)); | |
236 | } | |
237 | } | |
238 | ||
9e512045 SG |
239 | ofnode ofnode_first_subnode(ofnode node) |
240 | { | |
241 | assert(ofnode_valid(node)); | |
242 | if (ofnode_is_np(node)) | |
243 | return np_to_ofnode(node.np->child); | |
244 | ||
245 | return offset_to_ofnode( | |
246 | fdt_first_subnode(gd->fdt_blob, ofnode_to_offset(node))); | |
247 | } | |
248 | ||
249 | ofnode ofnode_next_subnode(ofnode node) | |
250 | { | |
251 | assert(ofnode_valid(node)); | |
252 | if (ofnode_is_np(node)) | |
253 | return np_to_ofnode(node.np->sibling); | |
254 | ||
255 | return offset_to_ofnode( | |
256 | fdt_next_subnode(gd->fdt_blob, ofnode_to_offset(node))); | |
257 | } | |
258 | ||
e2d5997f PT |
259 | ofnode ofnode_get_parent(ofnode node) |
260 | { | |
261 | ofnode parent; | |
262 | ||
263 | assert(ofnode_valid(node)); | |
264 | if (ofnode_is_np(node)) | |
265 | parent = np_to_ofnode(of_get_parent(ofnode_to_np(node))); | |
266 | else | |
267 | parent.of_offset = fdt_parent_offset(gd->fdt_blob, | |
268 | ofnode_to_offset(node)); | |
269 | ||
270 | return parent; | |
271 | } | |
272 | ||
9e512045 SG |
273 | const char *ofnode_get_name(ofnode node) |
274 | { | |
8f0a70e8 KY |
275 | if (!ofnode_valid(node)) { |
276 | debug("%s node not valid\n", __func__); | |
277 | return NULL; | |
278 | } | |
279 | ||
9e512045 SG |
280 | if (ofnode_is_np(node)) |
281 | return strrchr(node.np->full_name, '/') + 1; | |
282 | ||
283 | return fdt_get_name(gd->fdt_blob, ofnode_to_offset(node), NULL); | |
284 | } | |
285 | ||
b4f20767 KY |
286 | ofnode ofnode_get_by_phandle(uint phandle) |
287 | { | |
288 | ofnode node; | |
289 | ||
290 | if (of_live_active()) | |
291 | node = np_to_ofnode(of_find_node_by_phandle(phandle)); | |
292 | else | |
293 | node.of_offset = fdt_node_offset_by_phandle(gd->fdt_blob, | |
294 | phandle); | |
295 | ||
296 | return node; | |
297 | } | |
298 | ||
e679d03b | 299 | fdt_addr_t ofnode_get_addr_size_index(ofnode node, int index, fdt_size_t *size) |
bed77496 | 300 | { |
1678754f | 301 | int na, ns; |
1678754f | 302 | |
bed77496 SG |
303 | if (ofnode_is_np(node)) { |
304 | const __be32 *prop_val; | |
e18c41fc | 305 | u64 size64; |
bed77496 | 306 | uint flags; |
bed77496 | 307 | |
e18c41fc SG |
308 | prop_val = of_get_address(ofnode_to_np(node), index, &size64, |
309 | &flags); | |
bed77496 SG |
310 | if (!prop_val) |
311 | return FDT_ADDR_T_NONE; | |
e18c41fc SG |
312 | if (size) |
313 | *size = size64; | |
286ede65 | 314 | |
e8d52918 MS |
315 | ns = of_n_size_cells(ofnode_to_np(node)); |
316 | ||
317 | if (IS_ENABLED(CONFIG_OF_TRANSLATE) && ns > 0) { | |
286ede65 MS |
318 | return of_translate_address(ofnode_to_np(node), prop_val); |
319 | } else { | |
320 | na = of_n_addr_cells(ofnode_to_np(node)); | |
321 | return of_read_number(prop_val, na); | |
322 | } | |
bed77496 | 323 | } else { |
1678754f K |
324 | na = ofnode_read_simple_addr_cells(ofnode_get_parent(node)); |
325 | ns = ofnode_read_simple_size_cells(ofnode_get_parent(node)); | |
326 | return fdtdec_get_addr_size_fixed(gd->fdt_blob, | |
327 | ofnode_to_offset(node), "reg", | |
e679d03b | 328 | index, na, ns, size, true); |
bed77496 SG |
329 | } |
330 | ||
331 | return FDT_ADDR_T_NONE; | |
332 | } | |
333 | ||
e679d03b K |
334 | fdt_addr_t ofnode_get_addr_index(ofnode node, int index) |
335 | { | |
336 | fdt_size_t size; | |
337 | ||
338 | return ofnode_get_addr_size_index(node, index, &size); | |
339 | } | |
340 | ||
bed77496 SG |
341 | fdt_addr_t ofnode_get_addr(ofnode node) |
342 | { | |
343 | return ofnode_get_addr_index(node, 0); | |
344 | } | |
345 | ||
9e512045 SG |
346 | int ofnode_stringlist_search(ofnode node, const char *property, |
347 | const char *string) | |
348 | { | |
349 | if (ofnode_is_np(node)) { | |
350 | return of_property_match_string(ofnode_to_np(node), | |
351 | property, string); | |
352 | } else { | |
353 | int ret; | |
354 | ||
355 | ret = fdt_stringlist_search(gd->fdt_blob, | |
356 | ofnode_to_offset(node), property, | |
357 | string); | |
358 | if (ret == -FDT_ERR_NOTFOUND) | |
359 | return -ENODATA; | |
360 | else if (ret < 0) | |
361 | return -EINVAL; | |
362 | ||
363 | return ret; | |
364 | } | |
365 | } | |
366 | ||
367 | int ofnode_read_string_index(ofnode node, const char *property, int index, | |
368 | const char **outp) | |
369 | { | |
370 | if (ofnode_is_np(node)) { | |
371 | return of_property_read_string_index(ofnode_to_np(node), | |
372 | property, index, outp); | |
373 | } else { | |
374 | int len; | |
375 | ||
376 | *outp = fdt_stringlist_get(gd->fdt_blob, ofnode_to_offset(node), | |
377 | property, index, &len); | |
378 | if (len < 0) | |
379 | return -EINVAL; | |
380 | return 0; | |
381 | } | |
382 | } | |
383 | ||
8c293d6a SG |
384 | int ofnode_read_string_count(ofnode node, const char *property) |
385 | { | |
386 | if (ofnode_is_np(node)) { | |
387 | return of_property_count_strings(ofnode_to_np(node), property); | |
388 | } else { | |
389 | return fdt_stringlist_count(gd->fdt_blob, | |
390 | ofnode_to_offset(node), property); | |
391 | } | |
392 | } | |
393 | ||
9e512045 SG |
394 | static void ofnode_from_fdtdec_phandle_args(struct fdtdec_phandle_args *in, |
395 | struct ofnode_phandle_args *out) | |
396 | { | |
397 | assert(OF_MAX_PHANDLE_ARGS == MAX_PHANDLE_ARGS); | |
398 | out->node = offset_to_ofnode(in->node); | |
399 | out->args_count = in->args_count; | |
400 | memcpy(out->args, in->args, sizeof(out->args)); | |
401 | } | |
402 | ||
403 | static void ofnode_from_of_phandle_args(struct of_phandle_args *in, | |
404 | struct ofnode_phandle_args *out) | |
405 | { | |
406 | assert(OF_MAX_PHANDLE_ARGS == MAX_PHANDLE_ARGS); | |
407 | out->node = np_to_ofnode(in->np); | |
408 | out->args_count = in->args_count; | |
409 | memcpy(out->args, in->args, sizeof(out->args)); | |
410 | } | |
411 | ||
412 | int ofnode_parse_phandle_with_args(ofnode node, const char *list_name, | |
413 | const char *cells_name, int cell_count, | |
414 | int index, | |
415 | struct ofnode_phandle_args *out_args) | |
416 | { | |
417 | if (ofnode_is_np(node)) { | |
418 | struct of_phandle_args args; | |
419 | int ret; | |
420 | ||
421 | ret = of_parse_phandle_with_args(ofnode_to_np(node), | |
01d89e3d PD |
422 | list_name, cells_name, |
423 | cell_count, index, | |
51db287a | 424 | &args); |
9e512045 SG |
425 | if (ret) |
426 | return ret; | |
427 | ofnode_from_of_phandle_args(&args, out_args); | |
428 | } else { | |
429 | struct fdtdec_phandle_args args; | |
430 | int ret; | |
431 | ||
432 | ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, | |
51db287a MS |
433 | ofnode_to_offset(node), |
434 | list_name, cells_name, | |
435 | cell_count, index, &args); | |
9e512045 SG |
436 | if (ret) |
437 | return ret; | |
438 | ofnode_from_fdtdec_phandle_args(&args, out_args); | |
439 | } | |
440 | ||
441 | return 0; | |
442 | } | |
443 | ||
642346ae | 444 | int ofnode_count_phandle_with_args(ofnode node, const char *list_name, |
89f68302 | 445 | const char *cells_name, int cell_count) |
642346ae PC |
446 | { |
447 | if (ofnode_is_np(node)) | |
448 | return of_count_phandle_with_args(ofnode_to_np(node), | |
89f68302 | 449 | list_name, cells_name, cell_count); |
642346ae PC |
450 | else |
451 | return fdtdec_parse_phandle_with_args(gd->fdt_blob, | |
452 | ofnode_to_offset(node), list_name, cells_name, | |
89f68302 | 453 | cell_count, -1, NULL); |
642346ae PC |
454 | } |
455 | ||
9e512045 SG |
456 | ofnode ofnode_path(const char *path) |
457 | { | |
458 | if (of_live_active()) | |
459 | return np_to_ofnode(of_find_node_by_path(path)); | |
460 | else | |
461 | return offset_to_ofnode(fdt_path_offset(gd->fdt_blob, path)); | |
462 | } | |
463 | ||
bd933bfd | 464 | const void *ofnode_read_chosen_prop(const char *propname, int *sizep) |
9e512045 SG |
465 | { |
466 | ofnode chosen_node; | |
467 | ||
468 | chosen_node = ofnode_path("/chosen"); | |
469 | ||
bd933bfd SG |
470 | return ofnode_read_prop(chosen_node, propname, sizep); |
471 | } | |
472 | ||
473 | const char *ofnode_read_chosen_string(const char *propname) | |
474 | { | |
475 | return ofnode_read_chosen_prop(propname, NULL); | |
9e512045 SG |
476 | } |
477 | ||
478 | ofnode ofnode_get_chosen_node(const char *name) | |
479 | { | |
480 | const char *prop; | |
481 | ||
bd933bfd | 482 | prop = ofnode_read_chosen_prop(name, NULL); |
9e512045 SG |
483 | if (!prop) |
484 | return ofnode_null(); | |
485 | ||
486 | return ofnode_path(prop); | |
487 | } | |
488 | ||
305d3188 MS |
489 | const void *ofnode_read_aliases_prop(const char *propname, int *sizep) |
490 | { | |
491 | ofnode node; | |
492 | ||
493 | node = ofnode_path("/aliases"); | |
494 | ||
495 | return ofnode_read_prop(node, propname, sizep); | |
496 | } | |
497 | ||
498 | ofnode ofnode_get_aliases_node(const char *name) | |
499 | { | |
500 | const char *prop; | |
501 | ||
502 | prop = ofnode_read_aliases_prop(name, NULL); | |
503 | if (!prop) | |
504 | return ofnode_null(); | |
505 | ||
506 | debug("%s: node_path: %s\n", __func__, prop); | |
507 | ||
508 | return ofnode_path(prop); | |
509 | } | |
510 | ||
89b84b85 CY |
511 | int ofnode_get_child_count(ofnode parent) |
512 | { | |
513 | ofnode child; | |
514 | int num = 0; | |
515 | ||
516 | ofnode_for_each_subnode(child, parent) | |
517 | num++; | |
518 | ||
519 | return num; | |
520 | } | |
521 | ||
9e512045 SG |
522 | static int decode_timing_property(ofnode node, const char *name, |
523 | struct timing_entry *result) | |
524 | { | |
525 | int length, ret = 0; | |
526 | ||
527 | length = ofnode_read_size(node, name); | |
528 | if (length < 0) { | |
529 | debug("%s: could not find property %s\n", | |
530 | ofnode_get_name(node), name); | |
531 | return length; | |
532 | } | |
533 | ||
534 | if (length == sizeof(u32)) { | |
535 | result->typ = ofnode_read_u32_default(node, name, 0); | |
536 | result->min = result->typ; | |
537 | result->max = result->typ; | |
538 | } else { | |
539 | ret = ofnode_read_u32_array(node, name, &result->min, 3); | |
540 | } | |
541 | ||
542 | return ret; | |
543 | } | |
544 | ||
545 | int ofnode_decode_display_timing(ofnode parent, int index, | |
546 | struct display_timing *dt) | |
547 | { | |
548 | int i; | |
549 | ofnode timings, node; | |
550 | u32 val = 0; | |
551 | int ret = 0; | |
552 | ||
553 | timings = ofnode_find_subnode(parent, "display-timings"); | |
554 | if (!ofnode_valid(timings)) | |
555 | return -EINVAL; | |
556 | ||
3991f42e SG |
557 | i = 0; |
558 | ofnode_for_each_subnode(node, timings) { | |
559 | if (i++ == index) | |
560 | break; | |
561 | } | |
9e512045 SG |
562 | |
563 | if (!ofnode_valid(node)) | |
564 | return -EINVAL; | |
565 | ||
566 | memset(dt, 0, sizeof(*dt)); | |
567 | ||
568 | ret |= decode_timing_property(node, "hback-porch", &dt->hback_porch); | |
569 | ret |= decode_timing_property(node, "hfront-porch", &dt->hfront_porch); | |
570 | ret |= decode_timing_property(node, "hactive", &dt->hactive); | |
571 | ret |= decode_timing_property(node, "hsync-len", &dt->hsync_len); | |
572 | ret |= decode_timing_property(node, "vback-porch", &dt->vback_porch); | |
573 | ret |= decode_timing_property(node, "vfront-porch", &dt->vfront_porch); | |
574 | ret |= decode_timing_property(node, "vactive", &dt->vactive); | |
575 | ret |= decode_timing_property(node, "vsync-len", &dt->vsync_len); | |
576 | ret |= decode_timing_property(node, "clock-frequency", &dt->pixelclock); | |
577 | ||
578 | dt->flags = 0; | |
579 | val = ofnode_read_u32_default(node, "vsync-active", -1); | |
580 | if (val != -1) { | |
581 | dt->flags |= val ? DISPLAY_FLAGS_VSYNC_HIGH : | |
582 | DISPLAY_FLAGS_VSYNC_LOW; | |
583 | } | |
584 | val = ofnode_read_u32_default(node, "hsync-active", -1); | |
585 | if (val != -1) { | |
586 | dt->flags |= val ? DISPLAY_FLAGS_HSYNC_HIGH : | |
587 | DISPLAY_FLAGS_HSYNC_LOW; | |
588 | } | |
589 | val = ofnode_read_u32_default(node, "de-active", -1); | |
590 | if (val != -1) { | |
591 | dt->flags |= val ? DISPLAY_FLAGS_DE_HIGH : | |
592 | DISPLAY_FLAGS_DE_LOW; | |
593 | } | |
594 | val = ofnode_read_u32_default(node, "pixelclk-active", -1); | |
595 | if (val != -1) { | |
596 | dt->flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE : | |
597 | DISPLAY_FLAGS_PIXDATA_NEGEDGE; | |
598 | } | |
599 | ||
600 | if (ofnode_read_bool(node, "interlaced")) | |
601 | dt->flags |= DISPLAY_FLAGS_INTERLACED; | |
602 | if (ofnode_read_bool(node, "doublescan")) | |
603 | dt->flags |= DISPLAY_FLAGS_DOUBLESCAN; | |
604 | if (ofnode_read_bool(node, "doubleclk")) | |
605 | dt->flags |= DISPLAY_FLAGS_DOUBLECLK; | |
606 | ||
607 | return ret; | |
608 | } | |
609 | ||
61e51bab | 610 | const void *ofnode_get_property(ofnode node, const char *propname, int *lenp) |
9e512045 | 611 | { |
cb7dbe1f MY |
612 | if (ofnode_is_np(node)) |
613 | return of_get_property(ofnode_to_np(node), propname, lenp); | |
614 | else | |
9e512045 SG |
615 | return fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), |
616 | propname, lenp); | |
9e512045 SG |
617 | } |
618 | ||
ce891fca PD |
619 | int ofnode_get_first_property(ofnode node, struct ofprop *prop) |
620 | { | |
621 | prop->node = node; | |
622 | ||
623 | if (ofnode_is_np(node)) { | |
624 | prop->prop = of_get_first_property(ofnode_to_np(prop->node)); | |
625 | if (!prop->prop) | |
626 | return -FDT_ERR_NOTFOUND; | |
627 | } else { | |
628 | prop->offset = | |
629 | fdt_first_property_offset(gd->fdt_blob, | |
630 | ofnode_to_offset(prop->node)); | |
631 | if (prop->offset < 0) | |
632 | return prop->offset; | |
633 | } | |
634 | ||
635 | return 0; | |
636 | } | |
637 | ||
638 | int ofnode_get_next_property(struct ofprop *prop) | |
639 | { | |
640 | if (ofnode_is_np(prop->node)) { | |
641 | prop->prop = of_get_next_property(ofnode_to_np(prop->node), | |
642 | prop->prop); | |
643 | if (!prop->prop) | |
644 | return -FDT_ERR_NOTFOUND; | |
645 | } else { | |
646 | prop->offset = fdt_next_property_offset(gd->fdt_blob, | |
647 | prop->offset); | |
648 | if (prop->offset < 0) | |
649 | return prop->offset; | |
650 | } | |
651 | ||
652 | return 0; | |
653 | } | |
654 | ||
655 | const void *ofnode_get_property_by_prop(const struct ofprop *prop, | |
656 | const char **propname, int *lenp) | |
657 | { | |
658 | if (ofnode_is_np(prop->node)) | |
659 | return of_get_property_by_prop(ofnode_to_np(prop->node), | |
660 | prop->prop, propname, lenp); | |
661 | else | |
662 | return fdt_getprop_by_offset(gd->fdt_blob, | |
663 | prop->offset, | |
664 | propname, lenp); | |
665 | } | |
666 | ||
9e512045 SG |
667 | bool ofnode_is_available(ofnode node) |
668 | { | |
669 | if (ofnode_is_np(node)) | |
670 | return of_device_is_available(ofnode_to_np(node)); | |
671 | else | |
672 | return fdtdec_get_is_enabled(gd->fdt_blob, | |
673 | ofnode_to_offset(node)); | |
674 | } | |
675 | ||
676 | fdt_addr_t ofnode_get_addr_size(ofnode node, const char *property, | |
677 | fdt_size_t *sizep) | |
678 | { | |
679 | if (ofnode_is_np(node)) { | |
680 | int na, ns; | |
681 | int psize; | |
682 | const struct device_node *np = ofnode_to_np(node); | |
68a34529 | 683 | const __be32 *prop = of_get_property(np, property, &psize); |
9e512045 | 684 | |
68a34529 KG |
685 | if (!prop) |
686 | return FDT_ADDR_T_NONE; | |
9e512045 | 687 | na = of_n_addr_cells(np); |
51cb9275 | 688 | ns = of_n_size_cells(np); |
a4b8e372 | 689 | *sizep = of_read_number(prop + na, ns); |
a6a45cd3 | 690 | |
00333182 | 691 | if (CONFIG_IS_ENABLED(OF_TRANSLATE) && ns > 0) |
a6a45cd3 MV |
692 | return of_translate_address(np, prop); |
693 | else | |
694 | return of_read_number(prop, na); | |
9e512045 SG |
695 | } else { |
696 | return fdtdec_get_addr_size(gd->fdt_blob, | |
697 | ofnode_to_offset(node), property, | |
698 | sizep); | |
699 | } | |
700 | } | |
701 | ||
702 | const uint8_t *ofnode_read_u8_array_ptr(ofnode node, const char *propname, | |
703 | size_t sz) | |
704 | { | |
705 | if (ofnode_is_np(node)) { | |
706 | const struct device_node *np = ofnode_to_np(node); | |
707 | int psize; | |
708 | const __be32 *prop = of_get_property(np, propname, &psize); | |
709 | ||
710 | if (!prop || sz != psize) | |
711 | return NULL; | |
712 | return (uint8_t *)prop; | |
713 | ||
714 | } else { | |
715 | return fdtdec_locate_byte_array(gd->fdt_blob, | |
716 | ofnode_to_offset(node), propname, sz); | |
717 | } | |
718 | } | |
719 | ||
720 | int ofnode_read_pci_addr(ofnode node, enum fdt_pci_space type, | |
721 | const char *propname, struct fdt_pci_addr *addr) | |
722 | { | |
8c9eaada | 723 | const fdt32_t *cell; |
9e512045 SG |
724 | int len; |
725 | int ret = -ENOENT; | |
726 | ||
727 | debug("%s: %s: ", __func__, propname); | |
728 | ||
729 | /* | |
730 | * If we follow the pci bus bindings strictly, we should check | |
731 | * the value of the node's parent node's #address-cells and | |
732 | * #size-cells. They need to be 3 and 2 accordingly. However, | |
733 | * for simplicity we skip the check here. | |
734 | */ | |
61e51bab | 735 | cell = ofnode_get_property(node, propname, &len); |
9e512045 SG |
736 | if (!cell) |
737 | goto fail; | |
738 | ||
739 | if ((len % FDT_PCI_REG_SIZE) == 0) { | |
740 | int num = len / FDT_PCI_REG_SIZE; | |
741 | int i; | |
742 | ||
743 | for (i = 0; i < num; i++) { | |
744 | debug("pci address #%d: %08lx %08lx %08lx\n", i, | |
745 | (ulong)fdt32_to_cpu(cell[0]), | |
746 | (ulong)fdt32_to_cpu(cell[1]), | |
747 | (ulong)fdt32_to_cpu(cell[2])); | |
748 | if ((fdt32_to_cpu(*cell) & type) == type) { | |
749 | addr->phys_hi = fdt32_to_cpu(cell[0]); | |
750 | addr->phys_mid = fdt32_to_cpu(cell[1]); | |
e587886a | 751 | addr->phys_lo = fdt32_to_cpu(cell[2]); |
9e512045 | 752 | break; |
9e512045 | 753 | } |
51db287a MS |
754 | |
755 | cell += (FDT_PCI_ADDR_CELLS + | |
756 | FDT_PCI_SIZE_CELLS); | |
9e512045 SG |
757 | } |
758 | ||
759 | if (i == num) { | |
760 | ret = -ENXIO; | |
761 | goto fail; | |
762 | } | |
763 | ||
764 | return 0; | |
9e512045 SG |
765 | } |
766 | ||
51db287a MS |
767 | ret = -EINVAL; |
768 | ||
9e512045 SG |
769 | fail: |
770 | debug("(not found)\n"); | |
771 | return ret; | |
772 | } | |
773 | ||
7b9cbadd BM |
774 | int ofnode_read_pci_vendev(ofnode node, u16 *vendor, u16 *device) |
775 | { | |
776 | const char *list, *end; | |
777 | int len; | |
778 | ||
779 | list = ofnode_get_property(node, "compatible", &len); | |
780 | if (!list) | |
781 | return -ENOENT; | |
782 | ||
783 | end = list + len; | |
784 | while (list < end) { | |
785 | len = strlen(list); | |
786 | if (len >= strlen("pciVVVV,DDDD")) { | |
787 | char *s = strstr(list, "pci"); | |
788 | ||
789 | /* | |
790 | * check if the string is something like pciVVVV,DDDD.RR | |
791 | * or just pciVVVV,DDDD | |
792 | */ | |
793 | if (s && s[7] == ',' && | |
794 | (s[12] == '.' || s[12] == 0)) { | |
795 | s += 3; | |
796 | *vendor = simple_strtol(s, NULL, 16); | |
797 | ||
798 | s += 5; | |
799 | *device = simple_strtol(s, NULL, 16); | |
800 | ||
801 | return 0; | |
802 | } | |
803 | } | |
804 | list += (len + 1); | |
805 | } | |
806 | ||
807 | return -ENOENT; | |
808 | } | |
809 | ||
9e512045 SG |
810 | int ofnode_read_addr_cells(ofnode node) |
811 | { | |
ae6b33dc | 812 | if (ofnode_is_np(node)) { |
9e512045 | 813 | return of_n_addr_cells(ofnode_to_np(node)); |
ae6b33dc HS |
814 | } else { |
815 | int parent = fdt_parent_offset(gd->fdt_blob, | |
816 | ofnode_to_offset(node)); | |
817 | ||
818 | return fdt_address_cells(gd->fdt_blob, parent); | |
819 | } | |
9e512045 SG |
820 | } |
821 | ||
822 | int ofnode_read_size_cells(ofnode node) | |
823 | { | |
ae6b33dc | 824 | if (ofnode_is_np(node)) { |
9e512045 | 825 | return of_n_size_cells(ofnode_to_np(node)); |
ae6b33dc HS |
826 | } else { |
827 | int parent = fdt_parent_offset(gd->fdt_blob, | |
828 | ofnode_to_offset(node)); | |
829 | ||
830 | return fdt_size_cells(gd->fdt_blob, parent); | |
831 | } | |
878d68c0 SG |
832 | } |
833 | ||
834 | int ofnode_read_simple_addr_cells(ofnode node) | |
835 | { | |
836 | if (ofnode_is_np(node)) | |
837 | return of_simple_addr_cells(ofnode_to_np(node)); | |
838 | else | |
839 | return fdt_address_cells(gd->fdt_blob, ofnode_to_offset(node)); | |
840 | } | |
841 | ||
842 | int ofnode_read_simple_size_cells(ofnode node) | |
843 | { | |
844 | if (ofnode_is_np(node)) | |
845 | return of_simple_size_cells(ofnode_to_np(node)); | |
9e512045 SG |
846 | else |
847 | return fdt_size_cells(gd->fdt_blob, ofnode_to_offset(node)); | |
848 | } | |
849 | ||
850 | bool ofnode_pre_reloc(ofnode node) | |
851 | { | |
c7a88dae PD |
852 | #if defined(CONFIG_SPL_BUILD) || defined(CONFIG_TPL_BUILD) |
853 | /* for SPL and TPL the remaining nodes after the fdtgrep 1st pass | |
854 | * had property dm-pre-reloc or u-boot,dm-spl/tpl. | |
855 | * They are removed in final dtb (fdtgrep 2nd pass) | |
856 | */ | |
857 | return true; | |
858 | #else | |
252510ac | 859 | if (ofnode_read_bool(node, "u-boot,dm-pre-reloc")) |
9e512045 | 860 | return true; |
06f94461 SG |
861 | if (ofnode_read_bool(node, "u-boot,dm-pre-proper")) |
862 | return true; | |
9e512045 | 863 | |
9e512045 SG |
864 | /* |
865 | * In regular builds individual spl and tpl handling both | |
866 | * count as handled pre-relocation for later second init. | |
867 | */ | |
252510ac MY |
868 | if (ofnode_read_bool(node, "u-boot,dm-spl") || |
869 | ofnode_read_bool(node, "u-boot,dm-tpl")) | |
9e512045 | 870 | return true; |
9e512045 SG |
871 | |
872 | return false; | |
c7a88dae | 873 | #endif |
9e512045 | 874 | } |
dcf98852 SG |
875 | |
876 | int ofnode_read_resource(ofnode node, uint index, struct resource *res) | |
877 | { | |
878 | if (ofnode_is_np(node)) { | |
879 | return of_address_to_resource(ofnode_to_np(node), index, res); | |
880 | } else { | |
881 | struct fdt_resource fres; | |
882 | int ret; | |
883 | ||
884 | ret = fdt_get_resource(gd->fdt_blob, ofnode_to_offset(node), | |
885 | "reg", index, &fres); | |
886 | if (ret < 0) | |
887 | return -EINVAL; | |
888 | memset(res, '\0', sizeof(*res)); | |
889 | res->start = fres.start; | |
890 | res->end = fres.end; | |
891 | ||
892 | return 0; | |
893 | } | |
894 | } | |
7b8b47bd MY |
895 | |
896 | int ofnode_read_resource_byname(ofnode node, const char *name, | |
897 | struct resource *res) | |
898 | { | |
899 | int index; | |
900 | ||
901 | index = ofnode_stringlist_search(node, "reg-names", name); | |
902 | if (index < 0) | |
903 | return index; | |
904 | ||
905 | return ofnode_read_resource(node, index, res); | |
906 | } | |
147c6074 MS |
907 | |
908 | u64 ofnode_translate_address(ofnode node, const fdt32_t *in_addr) | |
909 | { | |
910 | if (ofnode_is_np(node)) | |
911 | return of_translate_address(ofnode_to_np(node), in_addr); | |
912 | else | |
913 | return fdt_translate_address(gd->fdt_blob, ofnode_to_offset(node), in_addr); | |
914 | } | |
5ccc2c21 | 915 | |
641067fb FD |
916 | u64 ofnode_translate_dma_address(ofnode node, const fdt32_t *in_addr) |
917 | { | |
918 | if (ofnode_is_np(node)) | |
919 | return of_translate_dma_address(ofnode_to_np(node), in_addr); | |
920 | else | |
921 | return fdt_translate_dma_address(gd->fdt_blob, ofnode_to_offset(node), in_addr); | |
922 | } | |
923 | ||
5ccc2c21 MY |
924 | int ofnode_device_is_compatible(ofnode node, const char *compat) |
925 | { | |
926 | if (ofnode_is_np(node)) | |
927 | return of_device_is_compatible(ofnode_to_np(node), compat, | |
928 | NULL, NULL); | |
929 | else | |
930 | return !fdt_node_check_compatible(gd->fdt_blob, | |
931 | ofnode_to_offset(node), | |
932 | compat); | |
933 | } | |
c60f671b SG |
934 | |
935 | ofnode ofnode_by_compatible(ofnode from, const char *compat) | |
936 | { | |
937 | if (of_live_active()) { | |
938 | return np_to_ofnode(of_find_compatible_node( | |
939 | (struct device_node *)ofnode_to_np(from), NULL, | |
940 | compat)); | |
941 | } else { | |
942 | return offset_to_ofnode(fdt_node_offset_by_compatible( | |
943 | gd->fdt_blob, ofnode_to_offset(from), compat)); | |
944 | } | |
945 | } | |
61fba0fa JW |
946 | |
947 | ofnode ofnode_by_prop_value(ofnode from, const char *propname, | |
948 | const void *propval, int proplen) | |
949 | { | |
950 | if (of_live_active()) { | |
951 | return np_to_ofnode(of_find_node_by_prop_value( | |
952 | (struct device_node *)ofnode_to_np(from), propname, | |
953 | propval, proplen)); | |
954 | } else { | |
955 | return offset_to_ofnode(fdt_node_offset_by_prop_value( | |
956 | gd->fdt_blob, ofnode_to_offset(from), | |
957 | propname, propval, proplen)); | |
958 | } | |
959 | } | |
e369e58d MS |
960 | |
961 | int ofnode_write_prop(ofnode node, const char *propname, int len, | |
962 | const void *value) | |
963 | { | |
964 | const struct device_node *np = ofnode_to_np(node); | |
965 | struct property *pp; | |
966 | struct property *pp_last = NULL; | |
967 | struct property *new; | |
968 | ||
969 | if (!of_live_active()) | |
970 | return -ENOSYS; | |
971 | ||
972 | if (!np) | |
973 | return -EINVAL; | |
974 | ||
975 | for (pp = np->properties; pp; pp = pp->next) { | |
976 | if (strcmp(pp->name, propname) == 0) { | |
977 | /* Property exists -> change value */ | |
978 | pp->value = (void *)value; | |
979 | pp->length = len; | |
980 | return 0; | |
981 | } | |
982 | pp_last = pp; | |
983 | } | |
984 | ||
985 | if (!pp_last) | |
986 | return -ENOENT; | |
987 | ||
988 | /* Property does not exist -> append new property */ | |
989 | new = malloc(sizeof(struct property)); | |
990 | if (!new) | |
991 | return -ENOMEM; | |
992 | ||
993 | new->name = strdup(propname); | |
205dd5af MS |
994 | if (!new->name) { |
995 | free(new); | |
e369e58d | 996 | return -ENOMEM; |
205dd5af | 997 | } |
e369e58d MS |
998 | |
999 | new->value = (void *)value; | |
1000 | new->length = len; | |
1001 | new->next = NULL; | |
1002 | ||
1003 | pp_last->next = new; | |
1004 | ||
1005 | return 0; | |
1006 | } | |
1007 | ||
1008 | int ofnode_write_string(ofnode node, const char *propname, const char *value) | |
1009 | { | |
1010 | if (!of_live_active()) | |
1011 | return -ENOSYS; | |
1012 | ||
1013 | assert(ofnode_valid(node)); | |
1014 | ||
1015 | debug("%s: %s = %s", __func__, propname, value); | |
1016 | ||
1017 | return ofnode_write_prop(node, propname, strlen(value) + 1, value); | |
1018 | } | |
1019 | ||
1020 | int ofnode_set_enabled(ofnode node, bool value) | |
1021 | { | |
1022 | if (!of_live_active()) | |
1023 | return -ENOSYS; | |
1024 | ||
1025 | assert(ofnode_valid(node)); | |
1026 | ||
1027 | if (value) | |
1028 | return ofnode_write_string(node, "status", "okay"); | |
1029 | else | |
16351212 | 1030 | return ofnode_write_string(node, "status", "disabled"); |
e369e58d | 1031 | } |