1
2
3
4
5
6
7
8
9#include <common.h>
10#include <env.h>
11#include <mapmem.h>
12#include <stdio_dev.h>
13#include <linux/ctype.h>
14#include <linux/types.h>
15#include <asm/global_data.h>
16#include <linux/libfdt.h>
17#include <fdt_support.h>
18#include <exports.h>
19#include <fdtdec.h>
20
21
22
23
24
25
26
27
28
29
30
31
32
33u32 fdt_getprop_u32_default_node(const void *fdt, int off, int cell,
34 const char *prop, const u32 dflt)
35{
36 const fdt32_t *val;
37 int len;
38
39 val = fdt_getprop(fdt, off, prop, &len);
40
41
42 if (!val)
43 return dflt;
44
45
46 if (len < ((cell + 1) * sizeof(uint32_t)))
47 return dflt;
48
49 return fdt32_to_cpu(*val);
50}
51
52
53
54
55
56
57
58
59
60
61
62
63u32 fdt_getprop_u32_default(const void *fdt, const char *path,
64 const char *prop, const u32 dflt)
65{
66 int off;
67
68 off = fdt_path_offset(fdt, path);
69 if (off < 0)
70 return dflt;
71
72 return fdt_getprop_u32_default_node(fdt, off, 0, prop, dflt);
73}
74
75
76
77
78
79
80
81
82
83
84
85
86
87int fdt_find_and_setprop(void *fdt, const char *node, const char *prop,
88 const void *val, int len, int create)
89{
90 int nodeoff = fdt_path_offset(fdt, node);
91
92 if (nodeoff < 0)
93 return nodeoff;
94
95 if ((!create) && (fdt_get_property(fdt, nodeoff, prop, NULL) == NULL))
96 return 0;
97
98 return fdt_setprop(fdt, nodeoff, prop, val, len);
99}
100
101
102
103
104
105
106
107
108
109
110
111int fdt_find_or_add_subnode(void *fdt, int parentoffset, const char *name)
112{
113 int offset;
114
115 offset = fdt_subnode_offset(fdt, parentoffset, name);
116
117 if (offset == -FDT_ERR_NOTFOUND)
118 offset = fdt_add_subnode(fdt, parentoffset, name);
119
120 if (offset < 0)
121 printf("%s: %s: %s\n", __func__, name, fdt_strerror(offset));
122
123 return offset;
124}
125
126
127#if defined(OF_STDOUT_PATH)
128static int fdt_fixup_stdout(void *fdt, int chosenoff)
129{
130 return fdt_setprop(fdt, chosenoff, "linux,stdout-path",
131 OF_STDOUT_PATH, strlen(OF_STDOUT_PATH) + 1);
132}
133#elif defined(CONFIG_OF_STDOUT_VIA_ALIAS) && defined(CONFIG_CONS_INDEX)
134static int fdt_fixup_stdout(void *fdt, int chosenoff)
135{
136 int err;
137 int aliasoff;
138 char sername[9] = { 0 };
139 const void *path;
140 int len;
141 char tmp[256];
142
143 sprintf(sername, "serial%d", CONFIG_CONS_INDEX - 1);
144
145 aliasoff = fdt_path_offset(fdt, "/aliases");
146 if (aliasoff < 0) {
147 err = aliasoff;
148 goto noalias;
149 }
150
151 path = fdt_getprop(fdt, aliasoff, sername, &len);
152 if (!path) {
153 err = len;
154 goto noalias;
155 }
156
157
158 memcpy(tmp, path, len);
159
160 err = fdt_setprop(fdt, chosenoff, "linux,stdout-path", tmp, len);
161 if (err < 0)
162 printf("WARNING: could not set linux,stdout-path %s.\n",
163 fdt_strerror(err));
164
165 return err;
166
167noalias:
168 printf("WARNING: %s: could not read %s alias: %s\n",
169 __func__, sername, fdt_strerror(err));
170
171 return 0;
172}
173#else
174static int fdt_fixup_stdout(void *fdt, int chosenoff)
175{
176 return 0;
177}
178#endif
179
180static inline int fdt_setprop_uxx(void *fdt, int nodeoffset, const char *name,
181 uint64_t val, int is_u64)
182{
183 if (is_u64)
184 return fdt_setprop_u64(fdt, nodeoffset, name, val);
185 else
186 return fdt_setprop_u32(fdt, nodeoffset, name, (uint32_t)val);
187}
188
189int fdt_root(void *fdt)
190{
191 char *serial;
192 int err;
193
194 err = fdt_check_header(fdt);
195 if (err < 0) {
196 printf("fdt_root: %s\n", fdt_strerror(err));
197 return err;
198 }
199
200 serial = env_get("serial#");
201 if (serial) {
202 err = fdt_setprop(fdt, 0, "serial-number", serial,
203 strlen(serial) + 1);
204
205 if (err < 0) {
206 printf("WARNING: could not set serial-number %s.\n",
207 fdt_strerror(err));
208 return err;
209 }
210 }
211
212 return 0;
213}
214
215int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end)
216{
217 int nodeoffset;
218 int err, j, total;
219 int is_u64;
220 uint64_t addr, size;
221
222
223 if (initrd_start == initrd_end)
224 return 0;
225
226
227 nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen");
228 if (nodeoffset < 0)
229 return nodeoffset;
230
231 total = fdt_num_mem_rsv(fdt);
232
233
234
235
236
237 for (j = 0; j < total; j++) {
238 err = fdt_get_mem_rsv(fdt, j, &addr, &size);
239 if (addr == initrd_start) {
240 fdt_del_mem_rsv(fdt, j);
241 break;
242 }
243 }
244
245 err = fdt_add_mem_rsv(fdt, initrd_start, initrd_end - initrd_start);
246 if (err < 0) {
247 printf("fdt_initrd: %s\n", fdt_strerror(err));
248 return err;
249 }
250
251 is_u64 = (fdt_address_cells(fdt, 0) == 2);
252
253 err = fdt_setprop_uxx(fdt, nodeoffset, "linux,initrd-start",
254 (uint64_t)initrd_start, is_u64);
255
256 if (err < 0) {
257 printf("WARNING: could not set linux,initrd-start %s.\n",
258 fdt_strerror(err));
259 return err;
260 }
261
262 err = fdt_setprop_uxx(fdt, nodeoffset, "linux,initrd-end",
263 (uint64_t)initrd_end, is_u64);
264
265 if (err < 0) {
266 printf("WARNING: could not set linux,initrd-end %s.\n",
267 fdt_strerror(err));
268
269 return err;
270 }
271
272 return 0;
273}
274
275int fdt_chosen(void *fdt)
276{
277 int nodeoffset;
278 int err;
279 char *str;
280
281 err = fdt_check_header(fdt);
282 if (err < 0) {
283 printf("fdt_chosen: %s\n", fdt_strerror(err));
284 return err;
285 }
286
287
288 nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen");
289 if (nodeoffset < 0)
290 return nodeoffset;
291
292 str = env_get("bootargs");
293 if (str) {
294 err = fdt_setprop(fdt, nodeoffset, "bootargs", str,
295 strlen(str) + 1);
296 if (err < 0) {
297 printf("WARNING: could not set bootargs %s.\n",
298 fdt_strerror(err));
299 return err;
300 }
301 }
302
303 return fdt_fixup_stdout(fdt, nodeoffset);
304}
305
306void do_fixup_by_path(void *fdt, const char *path, const char *prop,
307 const void *val, int len, int create)
308{
309#if defined(DEBUG)
310 int i;
311 debug("Updating property '%s/%s' = ", path, prop);
312 for (i = 0; i < len; i++)
313 debug(" %.2x", *(u8*)(val+i));
314 debug("\n");
315#endif
316 int rc = fdt_find_and_setprop(fdt, path, prop, val, len, create);
317 if (rc)
318 printf("Unable to update property %s:%s, err=%s\n",
319 path, prop, fdt_strerror(rc));
320}
321
322void do_fixup_by_path_u32(void *fdt, const char *path, const char *prop,
323 u32 val, int create)
324{
325 fdt32_t tmp = cpu_to_fdt32(val);
326 do_fixup_by_path(fdt, path, prop, &tmp, sizeof(tmp), create);
327}
328
329void do_fixup_by_prop(void *fdt,
330 const char *pname, const void *pval, int plen,
331 const char *prop, const void *val, int len,
332 int create)
333{
334 int off;
335#if defined(DEBUG)
336 int i;
337 debug("Updating property '%s' = ", prop);
338 for (i = 0; i < len; i++)
339 debug(" %.2x", *(u8*)(val+i));
340 debug("\n");
341#endif
342 off = fdt_node_offset_by_prop_value(fdt, -1, pname, pval, plen);
343 while (off != -FDT_ERR_NOTFOUND) {
344 if (create || (fdt_get_property(fdt, off, prop, NULL) != NULL))
345 fdt_setprop(fdt, off, prop, val, len);
346 off = fdt_node_offset_by_prop_value(fdt, off, pname, pval, plen);
347 }
348}
349
350void do_fixup_by_prop_u32(void *fdt,
351 const char *pname, const void *pval, int plen,
352 const char *prop, u32 val, int create)
353{
354 fdt32_t tmp = cpu_to_fdt32(val);
355 do_fixup_by_prop(fdt, pname, pval, plen, prop, &tmp, 4, create);
356}
357
358void do_fixup_by_compat(void *fdt, const char *compat,
359 const char *prop, const void *val, int len, int create)
360{
361 int off = -1;
362#if defined(DEBUG)
363 int i;
364 debug("Updating property '%s' = ", prop);
365 for (i = 0; i < len; i++)
366 debug(" %.2x", *(u8*)(val+i));
367 debug("\n");
368#endif
369 off = fdt_node_offset_by_compatible(fdt, -1, compat);
370 while (off != -FDT_ERR_NOTFOUND) {
371 if (create || (fdt_get_property(fdt, off, prop, NULL) != NULL))
372 fdt_setprop(fdt, off, prop, val, len);
373 off = fdt_node_offset_by_compatible(fdt, off, compat);
374 }
375}
376
377void do_fixup_by_compat_u32(void *fdt, const char *compat,
378 const char *prop, u32 val, int create)
379{
380 fdt32_t tmp = cpu_to_fdt32(val);
381 do_fixup_by_compat(fdt, compat, prop, &tmp, 4, create);
382}
383
384#ifdef CONFIG_ARCH_FIXUP_FDT_MEMORY
385
386
387
388static int fdt_pack_reg(const void *fdt, void *buf, u64 *address, u64 *size,
389 int n)
390{
391 int i;
392 int address_cells = fdt_address_cells(fdt, 0);
393 int size_cells = fdt_size_cells(fdt, 0);
394 char *p = buf;
395
396 for (i = 0; i < n; i++) {
397 if (address_cells == 2)
398 *(fdt64_t *)p = cpu_to_fdt64(address[i]);
399 else
400 *(fdt32_t *)p = cpu_to_fdt32(address[i]);
401 p += 4 * address_cells;
402
403 if (size_cells == 2)
404 *(fdt64_t *)p = cpu_to_fdt64(size[i]);
405 else
406 *(fdt32_t *)p = cpu_to_fdt32(size[i]);
407 p += 4 * size_cells;
408 }
409
410 return p - (char *)buf;
411}
412
413#if CONFIG_NR_DRAM_BANKS > 4
414#define MEMORY_BANKS_MAX CONFIG_NR_DRAM_BANKS
415#else
416#define MEMORY_BANKS_MAX 4
417#endif
418int fdt_fixup_memory_banks(void *blob, u64 start[], u64 size[], int banks)
419{
420 int err, nodeoffset;
421 int len, i;
422 u8 tmp[MEMORY_BANKS_MAX * 16];
423
424 if (banks > MEMORY_BANKS_MAX) {
425 printf("%s: num banks %d exceeds hardcoded limit %d."
426 " Recompile with higher MEMORY_BANKS_MAX?\n",
427 __FUNCTION__, banks, MEMORY_BANKS_MAX);
428 return -1;
429 }
430
431 err = fdt_check_header(blob);
432 if (err < 0) {
433 printf("%s: %s\n", __FUNCTION__, fdt_strerror(err));
434 return err;
435 }
436
437
438 nodeoffset = fdt_find_or_add_subnode(blob, 0, "memory");
439 if (nodeoffset < 0)
440 return nodeoffset;
441
442 err = fdt_setprop(blob, nodeoffset, "device_type", "memory",
443 sizeof("memory"));
444 if (err < 0) {
445 printf("WARNING: could not set %s %s.\n", "device_type",
446 fdt_strerror(err));
447 return err;
448 }
449
450 for (i = 0; i < banks; i++) {
451 if (start[i] == 0 && size[i] == 0)
452 break;
453 }
454
455 banks = i;
456
457 if (!banks)
458 return 0;
459
460 len = fdt_pack_reg(blob, tmp, start, size, banks);
461
462 err = fdt_setprop(blob, nodeoffset, "reg", tmp, len);
463 if (err < 0) {
464 printf("WARNING: could not set %s %s.\n",
465 "reg", fdt_strerror(err));
466 return err;
467 }
468 return 0;
469}
470#endif
471
472int fdt_fixup_memory(void *blob, u64 start, u64 size)
473{
474 return fdt_fixup_memory_banks(blob, &start, &size, 1);
475}
476
477void fdt_fixup_ethernet(void *fdt)
478{
479 int i = 0, j, prop;
480 char *tmp, *end;
481 char mac[16];
482 const char *path;
483 unsigned char mac_addr[ARP_HLEN];
484 int offset;
485#ifdef FDT_SEQ_MACADDR_FROM_ENV
486 int nodeoff;
487 const struct fdt_property *fdt_prop;
488#endif
489
490 if (fdt_path_offset(fdt, "/aliases") < 0)
491 return;
492
493
494 for (prop = 0; ; prop++) {
495 const char *name;
496
497
498 offset = fdt_first_property_offset(fdt,
499 fdt_path_offset(fdt, "/aliases"));
500
501 for (j = 0; j < prop; j++)
502 offset = fdt_next_property_offset(fdt, offset);
503
504 if (offset < 0)
505 break;
506
507 path = fdt_getprop_by_offset(fdt, offset, &name, NULL);
508 if (!strncmp(name, "ethernet", 8)) {
509
510 if (!strcmp(name, "ethernet")
511#ifdef FDT_SEQ_MACADDR_FROM_ENV
512 || !strcmp(name, "ethernet0")
513#endif
514 )
515 i = 0;
516#ifndef FDT_SEQ_MACADDR_FROM_ENV
517 else
518 i = trailing_strtol(name);
519#endif
520 if (i != -1) {
521 if (i == 0)
522 strcpy(mac, "ethaddr");
523 else
524 sprintf(mac, "eth%daddr", i);
525 } else {
526 continue;
527 }
528#ifdef FDT_SEQ_MACADDR_FROM_ENV
529 nodeoff = fdt_path_offset(fdt, path);
530 fdt_prop = fdt_get_property(fdt, nodeoff, "status",
531 NULL);
532 if (fdt_prop && !strcmp(fdt_prop->data, "disabled"))
533 continue;
534 i++;
535#endif
536 tmp = env_get(mac);
537 if (!tmp)
538 continue;
539
540 for (j = 0; j < 6; j++) {
541 mac_addr[j] = tmp ?
542 simple_strtoul(tmp, &end, 16) : 0;
543 if (tmp)
544 tmp = (*end) ? end + 1 : end;
545 }
546
547 do_fixup_by_path(fdt, path, "mac-address",
548 &mac_addr, 6, 0);
549 do_fixup_by_path(fdt, path, "local-mac-address",
550 &mac_addr, 6, 1);
551 }
552 }
553}
554
555int fdt_record_loadable(void *blob, u32 index, const char *name,
556 uintptr_t load_addr, u32 size, uintptr_t entry_point,
557 const char *type, const char *os)
558{
559 int err, node;
560
561 err = fdt_check_header(blob);
562 if (err < 0) {
563 printf("%s: %s\n", __func__, fdt_strerror(err));
564 return err;
565 }
566
567
568 node = fdt_find_or_add_subnode(blob, 0, "fit-images");
569 if (node < 0)
570 return node;
571
572
573 node = fdt_find_or_add_subnode(blob, node, name);
574 if (node < 0)
575 return node;
576
577
578
579
580
581
582 fdt_setprop_u32(blob, node, "load-addr", load_addr);
583 if (entry_point != -1)
584 fdt_setprop_u32(blob, node, "entry-point", entry_point);
585 fdt_setprop_u32(blob, node, "size", size);
586 if (type)
587 fdt_setprop_string(blob, node, "type", type);
588 if (os)
589 fdt_setprop_string(blob, node, "os", os);
590
591 return node;
592}
593
594
595int fdt_shrink_to_minimum(void *blob, uint extrasize)
596{
597 int i;
598 uint64_t addr, size;
599 int total, ret;
600 uint actualsize;
601 int fdt_memrsv = 0;
602
603 if (!blob)
604 return 0;
605
606 total = fdt_num_mem_rsv(blob);
607 for (i = 0; i < total; i++) {
608 fdt_get_mem_rsv(blob, i, &addr, &size);
609 if (addr == (uintptr_t)blob) {
610 fdt_del_mem_rsv(blob, i);
611 fdt_memrsv = 1;
612 break;
613 }
614 }
615
616
617
618
619
620
621
622 actualsize = fdt_off_dt_strings(blob) +
623 fdt_size_dt_strings(blob) + 5 * sizeof(struct fdt_reserve_entry);
624
625 actualsize += extrasize;
626
627 actualsize = ALIGN(actualsize + ((uintptr_t)blob & 0xfff), 0x1000);
628 actualsize = actualsize - ((uintptr_t)blob & 0xfff);
629
630
631 fdt_set_totalsize(blob, actualsize);
632
633 if (fdt_memrsv) {
634
635 ret = fdt_add_mem_rsv(blob, map_to_sysmem(blob), actualsize);
636 if (ret < 0)
637 return ret;
638 }
639
640 return actualsize;
641}
642
643#ifdef CONFIG_PCI
644#define CONFIG_SYS_PCI_NR_INBOUND_WIN 4
645
646#define FDT_PCI_PREFETCH (0x40000000)
647#define FDT_PCI_MEM32 (0x02000000)
648#define FDT_PCI_IO (0x01000000)
649#define FDT_PCI_MEM64 (0x03000000)
650
651int fdt_pci_dma_ranges(void *blob, int phb_off, struct pci_controller *hose) {
652
653 int addrcell, sizecell, len, r;
654 u32 *dma_range;
655
656 u32 dma_ranges[(3 + 2 + 2) * CONFIG_SYS_PCI_NR_INBOUND_WIN];
657
658 addrcell = fdt_getprop_u32_default(blob, "/", "#address-cells", 1);
659 sizecell = fdt_getprop_u32_default(blob, "/", "#size-cells", 1);
660
661 dma_range = &dma_ranges[0];
662 for (r = 0; r < hose->region_count; r++) {
663 u64 bus_start, phys_start, size;
664
665
666 if (!(hose->regions[r].flags & PCI_REGION_SYS_MEMORY))
667 continue;
668
669 bus_start = (u64)hose->regions[r].bus_start;
670 phys_start = (u64)hose->regions[r].phys_start;
671 size = (u64)hose->regions[r].size;
672
673 dma_range[0] = 0;
674 if (size >= 0x100000000ull)
675 dma_range[0] |= cpu_to_fdt32(FDT_PCI_MEM64);
676 else
677 dma_range[0] |= cpu_to_fdt32(FDT_PCI_MEM32);
678 if (hose->regions[r].flags & PCI_REGION_PREFETCH)
679 dma_range[0] |= cpu_to_fdt32(FDT_PCI_PREFETCH);
680#ifdef CONFIG_SYS_PCI_64BIT
681 dma_range[1] = cpu_to_fdt32(bus_start >> 32);
682#else
683 dma_range[1] = 0;
684#endif
685 dma_range[2] = cpu_to_fdt32(bus_start & 0xffffffff);
686
687 if (addrcell == 2) {
688 dma_range[3] = cpu_to_fdt32(phys_start >> 32);
689 dma_range[4] = cpu_to_fdt32(phys_start & 0xffffffff);
690 } else {
691 dma_range[3] = cpu_to_fdt32(phys_start & 0xffffffff);
692 }
693
694 if (sizecell == 2) {
695 dma_range[3 + addrcell + 0] =
696 cpu_to_fdt32(size >> 32);
697 dma_range[3 + addrcell + 1] =
698 cpu_to_fdt32(size & 0xffffffff);
699 } else {
700 dma_range[3 + addrcell + 0] =
701 cpu_to_fdt32(size & 0xffffffff);
702 }
703
704 dma_range += (3 + addrcell + sizecell);
705 }
706
707 len = dma_range - &dma_ranges[0];
708 if (len)
709 fdt_setprop(blob, phb_off, "dma-ranges", &dma_ranges[0], len*4);
710
711 return 0;
712}
713#endif
714
715int fdt_increase_size(void *fdt, int add_len)
716{
717 int newlen;
718
719 newlen = fdt_totalsize(fdt) + add_len;
720
721
722 return fdt_open_into(fdt, fdt, newlen);
723}
724
725#ifdef CONFIG_FDT_FIXUP_PARTITIONS
726#include <jffs2/load_kernel.h>
727#include <mtd_node.h>
728
729static int fdt_del_subnodes(const void *blob, int parent_offset)
730{
731 int off, ndepth;
732 int ret;
733
734 for (ndepth = 0, off = fdt_next_node(blob, parent_offset, &ndepth);
735 (off >= 0) && (ndepth > 0);
736 off = fdt_next_node(blob, off, &ndepth)) {
737 if (ndepth == 1) {
738 debug("delete %s: offset: %x\n",
739 fdt_get_name(blob, off, 0), off);
740 ret = fdt_del_node((void *)blob, off);
741 if (ret < 0) {
742 printf("Can't delete node: %s\n",
743 fdt_strerror(ret));
744 return ret;
745 } else {
746 ndepth = 0;
747 off = parent_offset;
748 }
749 }
750 }
751 return 0;
752}
753
754static int fdt_del_partitions(void *blob, int parent_offset)
755{
756 const void *prop;
757 int ndepth = 0;
758 int off;
759 int ret;
760
761 off = fdt_next_node(blob, parent_offset, &ndepth);
762 if (off > 0 && ndepth == 1) {
763 prop = fdt_getprop(blob, off, "label", NULL);
764 if (prop == NULL) {
765
766
767
768
769 return fdt_del_partitions(blob, off);
770 } else {
771 ret = fdt_del_subnodes(blob, parent_offset);
772 if (ret < 0) {
773 printf("Can't remove subnodes: %s\n",
774 fdt_strerror(ret));
775 return ret;
776 }
777 }
778 }
779 return 0;
780}
781
782int fdt_node_set_part_info(void *blob, int parent_offset,
783 struct mtd_device *dev)
784{
785 struct list_head *pentry;
786 struct part_info *part;
787 int off, ndepth = 0;
788 int part_num, ret;
789 int sizecell;
790 char buf[64];
791
792 ret = fdt_del_partitions(blob, parent_offset);
793 if (ret < 0)
794 return ret;
795
796
797
798
799
800 sizecell = fdt_getprop_u32_default_node(blob, parent_offset,
801 0, "#size-cells", 1);
802
803
804
805
806
807 off = fdt_next_node(blob, parent_offset, &ndepth);
808 if (off > 0 && ndepth == 1)
809 parent_offset = off;
810
811 part_num = 0;
812 list_for_each_prev(pentry, &dev->parts) {
813 int newoff;
814
815 part = list_entry(pentry, struct part_info, link);
816
817 debug("%2d: %-20s0x%08llx\t0x%08llx\t%d\n",
818 part_num, part->name, part->size,
819 part->offset, part->mask_flags);
820
821 sprintf(buf, "partition@%llx", part->offset);
822add_sub:
823 ret = fdt_add_subnode(blob, parent_offset, buf);
824 if (ret == -FDT_ERR_NOSPACE) {
825 ret = fdt_increase_size(blob, 512);
826 if (!ret)
827 goto add_sub;
828 else
829 goto err_size;
830 } else if (ret < 0) {
831 printf("Can't add partition node: %s\n",
832 fdt_strerror(ret));
833 return ret;
834 }
835 newoff = ret;
836
837
838 if (part->mask_flags & 1) {
839add_ro:
840 ret = fdt_setprop(blob, newoff, "read_only", NULL, 0);
841 if (ret == -FDT_ERR_NOSPACE) {
842 ret = fdt_increase_size(blob, 512);
843 if (!ret)
844 goto add_ro;
845 else
846 goto err_size;
847 } else if (ret < 0)
848 goto err_prop;
849 }
850
851add_reg:
852 if (sizecell == 2) {
853 ret = fdt_setprop_u64(blob, newoff,
854 "reg", part->offset);
855 if (!ret)
856 ret = fdt_appendprop_u64(blob, newoff,
857 "reg", part->size);
858 } else {
859 ret = fdt_setprop_u32(blob, newoff,
860 "reg", part->offset);
861 if (!ret)
862 ret = fdt_appendprop_u32(blob, newoff,
863 "reg", part->size);
864 }
865
866 if (ret == -FDT_ERR_NOSPACE) {
867 ret = fdt_increase_size(blob, 512);
868 if (!ret)
869 goto add_reg;
870 else
871 goto err_size;
872 } else if (ret < 0)
873 goto err_prop;
874
875add_label:
876 ret = fdt_setprop_string(blob, newoff, "label", part->name);
877 if (ret == -FDT_ERR_NOSPACE) {
878 ret = fdt_increase_size(blob, 512);
879 if (!ret)
880 goto add_label;
881 else
882 goto err_size;
883 } else if (ret < 0)
884 goto err_prop;
885
886 part_num++;
887 }
888 return 0;
889err_size:
890 printf("Can't increase blob size: %s\n", fdt_strerror(ret));
891 return ret;
892err_prop:
893 printf("Can't add property: %s\n", fdt_strerror(ret));
894 return ret;
895}
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911void fdt_fixup_mtdparts(void *blob, const struct node_info *node_info,
912 int node_info_size)
913{
914 struct mtd_device *dev;
915 int i, idx;
916 int noff;
917
918 if (mtdparts_init() != 0)
919 return;
920
921 for (i = 0; i < node_info_size; i++) {
922 idx = 0;
923 noff = fdt_node_offset_by_compatible(blob, -1,
924 node_info[i].compat);
925 while (noff != -FDT_ERR_NOTFOUND) {
926 debug("%s: %s, mtd dev type %d\n",
927 fdt_get_name(blob, noff, 0),
928 node_info[i].compat, node_info[i].type);
929 dev = device_find(node_info[i].type, idx++);
930 if (dev) {
931 if (fdt_node_set_part_info(blob, noff, dev))
932 return;
933 }
934
935
936 noff = fdt_node_offset_by_compatible(blob, noff,
937 node_info[i].compat);
938 }
939 }
940}
941#endif
942
943void fdt_del_node_and_alias(void *blob, const char *alias)
944{
945 int off = fdt_path_offset(blob, alias);
946
947 if (off < 0)
948 return;
949
950 fdt_del_node(blob, off);
951
952 off = fdt_path_offset(blob, "/aliases");
953 fdt_delprop(blob, off, alias);
954}
955
956
957#define OF_MAX_ADDR_CELLS 4
958#define OF_BAD_ADDR FDT_ADDR_T_NONE
959#define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \
960 (ns) > 0)
961
962
963#ifdef DEBUG
964static void of_dump_addr(const char *s, const fdt32_t *addr, int na)
965{
966 printf("%s", s);
967 while(na--)
968 printf(" %08x", *(addr++));
969 printf("\n");
970}
971#else
972static void of_dump_addr(const char *s, const fdt32_t *addr, int na) { }
973#endif
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005struct of_bus {
1006 const char *name;
1007 const char *addresses;
1008 int (*match)(const void *blob, int parentoffset);
1009 void (*count_cells)(const void *blob, int parentoffset,
1010 int *addrc, int *sizec);
1011 u64 (*map)(fdt32_t *addr, const fdt32_t *range,
1012 int na, int ns, int pna);
1013 int (*translate)(fdt32_t *addr, u64 offset, int na);
1014};
1015
1016
1017void fdt_support_default_count_cells(const void *blob, int parentoffset,
1018 int *addrc, int *sizec)
1019{
1020 const fdt32_t *prop;
1021
1022 if (addrc)
1023 *addrc = fdt_address_cells(blob, parentoffset);
1024
1025 if (sizec) {
1026 prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL);
1027 if (prop)
1028 *sizec = be32_to_cpup(prop);
1029 else
1030 *sizec = 1;
1031 }
1032}
1033
1034static u64 of_bus_default_map(fdt32_t *addr, const fdt32_t *range,
1035 int na, int ns, int pna)
1036{
1037 u64 cp, s, da;
1038
1039 cp = fdt_read_number(range, na);
1040 s = fdt_read_number(range + na + pna, ns);
1041 da = fdt_read_number(addr, na);
1042
1043 debug("OF: default map, cp=%llx, s=%llx, da=%llx\n", cp, s, da);
1044
1045 if (da < cp || da >= (cp + s))
1046 return OF_BAD_ADDR;
1047 return da - cp;
1048}
1049
1050static int of_bus_default_translate(fdt32_t *addr, u64 offset, int na)
1051{
1052 u64 a = fdt_read_number(addr, na);
1053 memset(addr, 0, na * 4);
1054 a += offset;
1055 if (na > 1)
1056 addr[na - 2] = cpu_to_fdt32(a >> 32);
1057 addr[na - 1] = cpu_to_fdt32(a & 0xffffffffu);
1058
1059 return 0;
1060}
1061
1062#ifdef CONFIG_OF_ISA_BUS
1063
1064
1065static int of_bus_isa_match(const void *blob, int parentoffset)
1066{
1067 const char *name;
1068
1069 name = fdt_get_name(blob, parentoffset, NULL);
1070 if (!name)
1071 return 0;
1072
1073 return !strcmp(name, "isa");
1074}
1075
1076static void of_bus_isa_count_cells(const void *blob, int parentoffset,
1077 int *addrc, int *sizec)
1078{
1079 if (addrc)
1080 *addrc = 2;
1081 if (sizec)
1082 *sizec = 1;
1083}
1084
1085static u64 of_bus_isa_map(fdt32_t *addr, const fdt32_t *range,
1086 int na, int ns, int pna)
1087{
1088 u64 cp, s, da;
1089
1090
1091 if ((addr[0] ^ range[0]) & cpu_to_be32(1))
1092 return OF_BAD_ADDR;
1093
1094 cp = fdt_read_number(range + 1, na - 1);
1095 s = fdt_read_number(range + na + pna, ns);
1096 da = fdt_read_number(addr + 1, na - 1);
1097
1098 debug("OF: ISA map, cp=%llx, s=%llx, da=%llx\n", cp, s, da);
1099
1100 if (da < cp || da >= (cp + s))
1101 return OF_BAD_ADDR;
1102 return da - cp;
1103}
1104
1105static int of_bus_isa_translate(fdt32_t *addr, u64 offset, int na)
1106{
1107 return of_bus_default_translate(addr + 1, offset, na - 1);
1108}
1109
1110#endif
1111
1112
1113static struct of_bus of_busses[] = {
1114#ifdef CONFIG_OF_ISA_BUS
1115
1116 {
1117 .name = "isa",
1118 .addresses = "reg",
1119 .match = of_bus_isa_match,
1120 .count_cells = of_bus_isa_count_cells,
1121 .map = of_bus_isa_map,
1122 .translate = of_bus_isa_translate,
1123 },
1124#endif
1125
1126 {
1127 .name = "default",
1128 .addresses = "reg",
1129 .count_cells = fdt_support_default_count_cells,
1130 .map = of_bus_default_map,
1131 .translate = of_bus_default_translate,
1132 },
1133};
1134
1135static struct of_bus *of_match_bus(const void *blob, int parentoffset)
1136{
1137 struct of_bus *bus;
1138
1139 if (ARRAY_SIZE(of_busses) == 1)
1140 return of_busses;
1141
1142 for (bus = of_busses; bus; bus++) {
1143 if (!bus->match || bus->match(blob, parentoffset))
1144 return bus;
1145 }
1146
1147
1148
1149
1150
1151
1152
1153 assert(0);
1154 return NULL;
1155}
1156
1157static int of_translate_one(const void *blob, int parent, struct of_bus *bus,
1158 struct of_bus *pbus, fdt32_t *addr,
1159 int na, int ns, int pna, const char *rprop)
1160{
1161 const fdt32_t *ranges;
1162 int rlen;
1163 int rone;
1164 u64 offset = OF_BAD_ADDR;
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178 ranges = fdt_getprop(blob, parent, rprop, &rlen);
1179 if (ranges == NULL || rlen == 0) {
1180 offset = fdt_read_number(addr, na);
1181 memset(addr, 0, pna * 4);
1182 debug("OF: no ranges, 1:1 translation\n");
1183 goto finish;
1184 }
1185
1186 debug("OF: walking ranges...\n");
1187
1188
1189 rlen /= 4;
1190 rone = na + pna + ns;
1191 for (; rlen >= rone; rlen -= rone, ranges += rone) {
1192 offset = bus->map(addr, ranges, na, ns, pna);
1193 if (offset != OF_BAD_ADDR)
1194 break;
1195 }
1196 if (offset == OF_BAD_ADDR) {
1197 debug("OF: not found !\n");
1198 return 1;
1199 }
1200 memcpy(addr, ranges + na, 4 * pna);
1201
1202 finish:
1203 of_dump_addr("OF: parent translation for:", addr, pna);
1204 debug("OF: with offset: %llu\n", offset);
1205
1206
1207 return pbus->translate(addr, offset, pna);
1208}
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220static u64 __of_translate_address(const void *blob, int node_offset,
1221 const fdt32_t *in_addr, const char *rprop)
1222{
1223 int parent;
1224 struct of_bus *bus, *pbus;
1225 fdt32_t addr[OF_MAX_ADDR_CELLS];
1226 int na, ns, pna, pns;
1227 u64 result = OF_BAD_ADDR;
1228
1229 debug("OF: ** translation for device %s **\n",
1230 fdt_get_name(blob, node_offset, NULL));
1231
1232
1233 parent = fdt_parent_offset(blob, node_offset);
1234 if (parent < 0)
1235 goto bail;
1236 bus = of_match_bus(blob, parent);
1237
1238
1239 bus->count_cells(blob, parent, &na, &ns);
1240 if (!OF_CHECK_COUNTS(na, ns)) {
1241 printf("%s: Bad cell count for %s\n", __FUNCTION__,
1242 fdt_get_name(blob, node_offset, NULL));
1243 goto bail;
1244 }
1245 memcpy(addr, in_addr, na * 4);
1246
1247 debug("OF: bus is %s (na=%d, ns=%d) on %s\n",
1248 bus->name, na, ns, fdt_get_name(blob, parent, NULL));
1249 of_dump_addr("OF: translating address:", addr, na);
1250
1251
1252 for (;;) {
1253
1254 node_offset = parent;
1255 parent = fdt_parent_offset(blob, node_offset);
1256
1257
1258 if (parent < 0) {
1259 debug("OF: reached root node\n");
1260 result = fdt_read_number(addr, na);
1261 break;
1262 }
1263
1264
1265 pbus = of_match_bus(blob, parent);
1266 pbus->count_cells(blob, parent, &pna, &pns);
1267 if (!OF_CHECK_COUNTS(pna, pns)) {
1268 printf("%s: Bad cell count for %s\n", __FUNCTION__,
1269 fdt_get_name(blob, node_offset, NULL));
1270 break;
1271 }
1272
1273 debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n",
1274 pbus->name, pna, pns, fdt_get_name(blob, parent, NULL));
1275
1276
1277 if (of_translate_one(blob, node_offset, bus, pbus,
1278 addr, na, ns, pna, rprop))
1279 break;
1280
1281
1282 na = pna;
1283 ns = pns;
1284 bus = pbus;
1285
1286 of_dump_addr("OF: one level translation:", addr, na);
1287 }
1288 bail:
1289
1290 return result;
1291}
1292
1293u64 fdt_translate_address(const void *blob, int node_offset,
1294 const fdt32_t *in_addr)
1295{
1296 return __of_translate_address(blob, node_offset, in_addr, "ranges");
1297}
1298
1299u64 fdt_translate_dma_address(const void *blob, int node_offset,
1300 const fdt32_t *in_addr)
1301{
1302 return __of_translate_address(blob, node_offset, in_addr, "dma-ranges");
1303}
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314int fdt_node_offset_by_compat_reg(void *blob, const char *compat,
1315 phys_addr_t compat_off)
1316{
1317 int len, off = fdt_node_offset_by_compatible(blob, -1, compat);
1318 while (off != -FDT_ERR_NOTFOUND) {
1319 const fdt32_t *reg = fdt_getprop(blob, off, "reg", &len);
1320 if (reg) {
1321 if (compat_off == fdt_translate_address(blob, off, reg))
1322 return off;
1323 }
1324 off = fdt_node_offset_by_compatible(blob, off, compat);
1325 }
1326
1327 return -FDT_ERR_NOTFOUND;
1328}
1329
1330
1331
1332
1333
1334
1335int fdt_alloc_phandle(void *blob)
1336{
1337 int offset;
1338 uint32_t phandle = 0;
1339
1340 for (offset = fdt_next_node(blob, -1, NULL); offset >= 0;
1341 offset = fdt_next_node(blob, offset, NULL)) {
1342 phandle = max(phandle, fdt_get_phandle(blob, offset));
1343 }
1344
1345 return phandle + 1;
1346}
1347
1348
1349
1350
1351
1352
1353
1354
1355int fdt_set_phandle(void *fdt, int nodeoffset, uint32_t phandle)
1356{
1357 int ret;
1358
1359#ifdef DEBUG
1360 int off = fdt_node_offset_by_phandle(fdt, phandle);
1361
1362 if ((off >= 0) && (off != nodeoffset)) {
1363 char buf[64];
1364
1365 fdt_get_path(fdt, nodeoffset, buf, sizeof(buf));
1366 printf("Trying to update node %s with phandle %u ",
1367 buf, phandle);
1368
1369 fdt_get_path(fdt, off, buf, sizeof(buf));
1370 printf("that already exists in node %s.\n", buf);
1371 return -FDT_ERR_BADPHANDLE;
1372 }
1373#endif
1374
1375 ret = fdt_setprop_cell(fdt, nodeoffset, "phandle", phandle);
1376 if (ret < 0)
1377 return ret;
1378
1379
1380
1381
1382
1383 ret = fdt_setprop_cell(fdt, nodeoffset, "linux,phandle", phandle);
1384
1385 return ret;
1386}
1387
1388
1389
1390
1391
1392
1393
1394unsigned int fdt_create_phandle(void *fdt, int nodeoffset)
1395{
1396
1397 int phandle = fdt_get_phandle(fdt, nodeoffset);
1398
1399
1400 if (phandle == 0) {
1401 int ret;
1402
1403 phandle = fdt_alloc_phandle(fdt);
1404 ret = fdt_set_phandle(fdt, nodeoffset, phandle);
1405 if (ret < 0) {
1406 printf("Can't set phandle %u: %s\n", phandle,
1407 fdt_strerror(ret));
1408 return 0;
1409 }
1410 }
1411
1412 return phandle;
1413}
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424int fdt_set_node_status(void *fdt, int nodeoffset,
1425 enum fdt_status status, unsigned int error_code)
1426{
1427 char buf[16];
1428 int ret = 0;
1429
1430 if (nodeoffset < 0)
1431 return nodeoffset;
1432
1433 switch (status) {
1434 case FDT_STATUS_OKAY:
1435 ret = fdt_setprop_string(fdt, nodeoffset, "status", "okay");
1436 break;
1437 case FDT_STATUS_DISABLED:
1438 ret = fdt_setprop_string(fdt, nodeoffset, "status", "disabled");
1439 break;
1440 case FDT_STATUS_FAIL:
1441 ret = fdt_setprop_string(fdt, nodeoffset, "status", "fail");
1442 break;
1443 case FDT_STATUS_FAIL_ERROR_CODE:
1444 sprintf(buf, "fail-%d", error_code);
1445 ret = fdt_setprop_string(fdt, nodeoffset, "status", buf);
1446 break;
1447 default:
1448 printf("Invalid fdt status: %x\n", status);
1449 ret = -1;
1450 break;
1451 }
1452
1453 return ret;
1454}
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465int fdt_set_status_by_alias(void *fdt, const char* alias,
1466 enum fdt_status status, unsigned int error_code)
1467{
1468 int offset = fdt_path_offset(fdt, alias);
1469
1470 return fdt_set_node_status(fdt, offset, status, error_code);
1471}
1472
1473#if defined(CONFIG_VIDEO) || defined(CONFIG_LCD)
1474int fdt_add_edid(void *blob, const char *compat, unsigned char *edid_buf)
1475{
1476 int noff;
1477 int ret;
1478
1479 noff = fdt_node_offset_by_compatible(blob, -1, compat);
1480 if (noff != -FDT_ERR_NOTFOUND) {
1481 debug("%s: %s\n", fdt_get_name(blob, noff, 0), compat);
1482add_edid:
1483 ret = fdt_setprop(blob, noff, "edid", edid_buf, 128);
1484 if (ret == -FDT_ERR_NOSPACE) {
1485 ret = fdt_increase_size(blob, 512);
1486 if (!ret)
1487 goto add_edid;
1488 else
1489 goto err_size;
1490 } else if (ret < 0) {
1491 printf("Can't add property: %s\n", fdt_strerror(ret));
1492 return ret;
1493 }
1494 }
1495 return 0;
1496err_size:
1497 printf("Can't increase blob size: %s\n", fdt_strerror(ret));
1498 return ret;
1499}
1500#endif
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511int fdt_verify_alias_address(void *fdt, int anode, const char *alias, u64 addr)
1512{
1513 const char *path;
1514 const fdt32_t *reg;
1515 int node, len;
1516 u64 dt_addr;
1517
1518 path = fdt_getprop(fdt, anode, alias, NULL);
1519 if (!path) {
1520
1521 return 1;
1522 }
1523
1524 node = fdt_path_offset(fdt, path);
1525 if (node < 0) {
1526 printf("Warning: device tree alias '%s' points to invalid "
1527 "node %s.\n", alias, path);
1528 return 0;
1529 }
1530
1531 reg = fdt_getprop(fdt, node, "reg", &len);
1532 if (!reg) {
1533 printf("Warning: device tree node '%s' has no address.\n",
1534 path);
1535 return 0;
1536 }
1537
1538 dt_addr = fdt_translate_address(fdt, node, reg);
1539 if (addr != dt_addr) {
1540 printf("Warning: U-Boot configured device %s at address %llu,\n"
1541 "but the device tree has it address %llx.\n",
1542 alias, addr, dt_addr);
1543 return 0;
1544 }
1545
1546 return 1;
1547}
1548
1549
1550
1551
1552u64 fdt_get_base_address(const void *fdt, int node)
1553{
1554 int size;
1555 const fdt32_t *prop;
1556
1557 prop = fdt_getprop(fdt, node, "reg", &size);
1558
1559 return prop ? fdt_translate_address(fdt, node, prop) : OF_BAD_ADDR;
1560}
1561
1562
1563
1564
1565static int fdt_read_prop(const fdt32_t *prop, int prop_len, int cell_off,
1566 uint64_t *val, int cells)
1567{
1568 const fdt32_t *prop32 = &prop[cell_off];
1569 const fdt64_t *prop64 = (const fdt64_t *)&prop[cell_off];
1570
1571 if ((cell_off + cells) > prop_len)
1572 return -FDT_ERR_NOSPACE;
1573
1574 switch (cells) {
1575 case 1:
1576 *val = fdt32_to_cpu(*prop32);
1577 break;
1578 case 2:
1579 *val = fdt64_to_cpu(*prop64);
1580 break;
1581 default:
1582 return -FDT_ERR_NOSPACE;
1583 }
1584
1585 return 0;
1586}
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601int fdt_read_range(void *fdt, int node, int n, uint64_t *child_addr,
1602 uint64_t *addr, uint64_t *len)
1603{
1604 int pnode = fdt_parent_offset(fdt, node);
1605 const fdt32_t *ranges;
1606 int pacells;
1607 int acells;
1608 int scells;
1609 int ranges_len;
1610 int cell = 0;
1611 int r = 0;
1612
1613
1614
1615
1616
1617
1618
1619 pacells = fdt_getprop_u32_default_node(fdt, pnode, 0, "#address-cells", 1);
1620 acells = fdt_getprop_u32_default_node(fdt, node, 0, "#address-cells", 1);
1621 scells = fdt_getprop_u32_default_node(fdt, node, 0, "#size-cells", 1);
1622
1623
1624 ranges = fdt_getprop(fdt, node, "ranges", &ranges_len);
1625 if (!ranges)
1626 return -FDT_ERR_NOTFOUND;
1627 ranges_len /= sizeof(uint32_t);
1628
1629
1630 cell = n * (pacells + acells + scells);
1631
1632
1633 if (child_addr) {
1634 r = fdt_read_prop(ranges, ranges_len, cell, child_addr,
1635 acells);
1636 if (r)
1637 return r;
1638 }
1639 cell += acells;
1640
1641
1642 if (addr)
1643 *addr = fdt_translate_address(fdt, node, ranges + cell);
1644 cell += pacells;
1645
1646
1647 if (len) {
1648 r = fdt_read_prop(ranges, ranges_len, cell, len, scells);
1649 if (r)
1650 return r;
1651 }
1652
1653 return 0;
1654}
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669int fdt_setup_simplefb_node(void *fdt, int node, u64 base_address, u32 width,
1670 u32 height, u32 stride, const char *format)
1671{
1672 char name[32];
1673 fdt32_t cells[4];
1674 int i, addrc, sizec, ret;
1675
1676 fdt_support_default_count_cells(fdt, fdt_parent_offset(fdt, node),
1677 &addrc, &sizec);
1678 i = 0;
1679 if (addrc == 2)
1680 cells[i++] = cpu_to_fdt32(base_address >> 32);
1681 cells[i++] = cpu_to_fdt32(base_address);
1682 if (sizec == 2)
1683 cells[i++] = 0;
1684 cells[i++] = cpu_to_fdt32(height * stride);
1685
1686 ret = fdt_setprop(fdt, node, "reg", cells, sizeof(cells[0]) * i);
1687 if (ret < 0)
1688 return ret;
1689
1690 snprintf(name, sizeof(name), "framebuffer@%llx", base_address);
1691 ret = fdt_set_name(fdt, node, name);
1692 if (ret < 0)
1693 return ret;
1694
1695 ret = fdt_setprop_u32(fdt, node, "width", width);
1696 if (ret < 0)
1697 return ret;
1698
1699 ret = fdt_setprop_u32(fdt, node, "height", height);
1700 if (ret < 0)
1701 return ret;
1702
1703 ret = fdt_setprop_u32(fdt, node, "stride", stride);
1704 if (ret < 0)
1705 return ret;
1706
1707 ret = fdt_setprop_string(fdt, node, "format", format);
1708 if (ret < 0)
1709 return ret;
1710
1711 ret = fdt_setprop_string(fdt, node, "status", "okay");
1712 if (ret < 0)
1713 return ret;
1714
1715 return 0;
1716}
1717
1718
1719
1720
1721
1722int fdt_fixup_display(void *blob, const char *path, const char *display)
1723{
1724 int off, toff;
1725
1726 if (!display || !path)
1727 return -FDT_ERR_NOTFOUND;
1728
1729 toff = fdt_path_offset(blob, path);
1730 if (toff >= 0)
1731 toff = fdt_subnode_offset(blob, toff, "display-timings");
1732 if (toff < 0)
1733 return toff;
1734
1735 for (off = fdt_first_subnode(blob, toff);
1736 off >= 0;
1737 off = fdt_next_subnode(blob, off)) {
1738 uint32_t h = fdt_get_phandle(blob, off);
1739 debug("%s:0x%x\n", fdt_get_name(blob, off, NULL),
1740 fdt32_to_cpu(h));
1741 if (strcasecmp(fdt_get_name(blob, off, NULL), display) == 0)
1742 return fdt_setprop_u32(blob, toff, "native-mode", h);
1743 }
1744 return toff;
1745}
1746
1747#ifdef CONFIG_OF_LIBFDT_OVERLAY
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757int fdt_overlay_apply_verbose(void *fdt, void *fdto)
1758{
1759 int err;
1760 bool has_symbols;
1761
1762 err = fdt_path_offset(fdt, "/__symbols__");
1763 has_symbols = err >= 0;
1764
1765 err = fdt_overlay_apply(fdt, fdto);
1766 if (err < 0) {
1767 printf("failed on fdt_overlay_apply(): %s\n",
1768 fdt_strerror(err));
1769 if (!has_symbols) {
1770 printf("base fdt does did not have a /__symbols__ node\n");
1771 printf("make sure you've compiled with -@\n");
1772 }
1773 }
1774 return err;
1775}
1776#endif
1777