1
2
3
4
5
6
7
8
9
10#include <common.h>
11#include <inttypes.h>
12#include <stdio_dev.h>
13#include <linux/ctype.h>
14#include <linux/types.h>
15#include <asm/global_data.h>
16#include <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 = getenv("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 = getenv("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#ifdef CONFIG_NR_DRAM_BANKS
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;
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 if (!banks)
451 return 0;
452
453 len = fdt_pack_reg(blob, tmp, start, size, banks);
454
455 err = fdt_setprop(blob, nodeoffset, "reg", tmp, len);
456 if (err < 0) {
457 printf("WARNING: could not set %s %s.\n",
458 "reg", fdt_strerror(err));
459 return err;
460 }
461 return 0;
462}
463#endif
464
465int fdt_fixup_memory(void *blob, u64 start, u64 size)
466{
467 return fdt_fixup_memory_banks(blob, &start, &size, 1);
468}
469
470void fdt_fixup_ethernet(void *fdt)
471{
472 int i, j, prop;
473 char *tmp, *end;
474 char mac[16];
475 const char *path;
476 unsigned char mac_addr[6];
477 int offset;
478
479 if (fdt_path_offset(fdt, "/aliases") < 0)
480 return;
481
482
483 for (prop = 0; ; prop++) {
484 const char *name;
485 int len = strlen("ethernet");
486
487
488 offset = fdt_first_property_offset(fdt,
489 fdt_path_offset(fdt, "/aliases"));
490
491 for (i = 0; i < prop; i++)
492 offset = fdt_next_property_offset(fdt, offset);
493
494 if (offset < 0)
495 break;
496
497 path = fdt_getprop_by_offset(fdt, offset, &name, NULL);
498 if (!strncmp(name, "ethernet", len)) {
499 i = trailing_strtol(name);
500 if (i != -1) {
501 if (i == 0)
502 strcpy(mac, "ethaddr");
503 else
504 sprintf(mac, "eth%daddr", i);
505 } else {
506 continue;
507 }
508 tmp = getenv(mac);
509 if (!tmp)
510 continue;
511
512 for (j = 0; j < 6; j++) {
513 mac_addr[j] = tmp ?
514 simple_strtoul(tmp, &end, 16) : 0;
515 if (tmp)
516 tmp = (*end) ? end + 1 : end;
517 }
518
519 do_fixup_by_path(fdt, path, "mac-address",
520 &mac_addr, 6, 0);
521 do_fixup_by_path(fdt, path, "local-mac-address",
522 &mac_addr, 6, 1);
523 }
524 }
525}
526
527
528int fdt_shrink_to_minimum(void *blob, uint extrasize)
529{
530 int i;
531 uint64_t addr, size;
532 int total, ret;
533 uint actualsize;
534
535 if (!blob)
536 return 0;
537
538 total = fdt_num_mem_rsv(blob);
539 for (i = 0; i < total; i++) {
540 fdt_get_mem_rsv(blob, i, &addr, &size);
541 if (addr == (uintptr_t)blob) {
542 fdt_del_mem_rsv(blob, i);
543 break;
544 }
545 }
546
547
548
549
550
551
552
553 actualsize = fdt_off_dt_strings(blob) +
554 fdt_size_dt_strings(blob) + 5 * sizeof(struct fdt_reserve_entry);
555
556 actualsize += extrasize;
557
558 actualsize = ALIGN(actualsize + ((uintptr_t)blob & 0xfff), 0x1000);
559 actualsize = actualsize - ((uintptr_t)blob & 0xfff);
560
561
562 fdt_set_totalsize(blob, actualsize);
563
564
565 ret = fdt_add_mem_rsv(blob, (uintptr_t)blob, actualsize);
566 if (ret < 0)
567 return ret;
568
569 return actualsize;
570}
571
572#ifdef CONFIG_PCI
573#define CONFIG_SYS_PCI_NR_INBOUND_WIN 4
574
575#define FDT_PCI_PREFETCH (0x40000000)
576#define FDT_PCI_MEM32 (0x02000000)
577#define FDT_PCI_IO (0x01000000)
578#define FDT_PCI_MEM64 (0x03000000)
579
580int fdt_pci_dma_ranges(void *blob, int phb_off, struct pci_controller *hose) {
581
582 int addrcell, sizecell, len, r;
583 u32 *dma_range;
584
585 u32 dma_ranges[(3 + 2 + 2) * CONFIG_SYS_PCI_NR_INBOUND_WIN];
586
587 addrcell = fdt_getprop_u32_default(blob, "/", "#address-cells", 1);
588 sizecell = fdt_getprop_u32_default(blob, "/", "#size-cells", 1);
589
590 dma_range = &dma_ranges[0];
591 for (r = 0; r < hose->region_count; r++) {
592 u64 bus_start, phys_start, size;
593
594
595 if (!(hose->regions[r].flags & PCI_REGION_SYS_MEMORY))
596 continue;
597
598 bus_start = (u64)hose->regions[r].bus_start;
599 phys_start = (u64)hose->regions[r].phys_start;
600 size = (u64)hose->regions[r].size;
601
602 dma_range[0] = 0;
603 if (size >= 0x100000000ull)
604 dma_range[0] |= FDT_PCI_MEM64;
605 else
606 dma_range[0] |= FDT_PCI_MEM32;
607 if (hose->regions[r].flags & PCI_REGION_PREFETCH)
608 dma_range[0] |= FDT_PCI_PREFETCH;
609#ifdef CONFIG_SYS_PCI_64BIT
610 dma_range[1] = bus_start >> 32;
611#else
612 dma_range[1] = 0;
613#endif
614 dma_range[2] = bus_start & 0xffffffff;
615
616 if (addrcell == 2) {
617 dma_range[3] = phys_start >> 32;
618 dma_range[4] = phys_start & 0xffffffff;
619 } else {
620 dma_range[3] = phys_start & 0xffffffff;
621 }
622
623 if (sizecell == 2) {
624 dma_range[3 + addrcell + 0] = size >> 32;
625 dma_range[3 + addrcell + 1] = size & 0xffffffff;
626 } else {
627 dma_range[3 + addrcell + 0] = size & 0xffffffff;
628 }
629
630 dma_range += (3 + addrcell + sizecell);
631 }
632
633 len = dma_range - &dma_ranges[0];
634 if (len)
635 fdt_setprop(blob, phb_off, "dma-ranges", &dma_ranges[0], len*4);
636
637 return 0;
638}
639#endif
640
641#ifdef CONFIG_FDT_FIXUP_NOR_FLASH_SIZE
642
643
644
645
646
647u32 __flash_get_bank_size(int cs, int idx)
648{
649 extern flash_info_t flash_info[];
650
651
652
653
654
655
656 return flash_info[cs].size;
657}
658u32 flash_get_bank_size(int cs, int idx)
659 __attribute__((weak, alias("__flash_get_bank_size")));
660
661
662
663
664
665
666int fdt_fixup_nor_flash_size(void *blob)
667{
668 char compat[][16] = { "cfi-flash", "jedec-flash" };
669 int off;
670 int len;
671 struct fdt_property *prop;
672 u32 *reg, *reg2;
673 int i;
674
675 for (i = 0; i < 2; i++) {
676 off = fdt_node_offset_by_compatible(blob, -1, compat[i]);
677 while (off != -FDT_ERR_NOTFOUND) {
678 int idx;
679
680
681
682
683
684 prop = fdt_get_property_w(blob, off, "reg", &len);
685 if (prop) {
686 int tuple_size = 3 * sizeof(reg);
687
688
689
690
691
692 reg = reg2 = (u32 *)&prop->data[0];
693 for (idx = 0; idx < (len / tuple_size); idx++) {
694
695
696
697 reg[2] = flash_get_bank_size(reg[0],
698 idx);
699
700
701
702
703 reg += 3;
704 }
705
706 fdt_setprop(blob, off, "reg", reg2, len);
707 }
708
709
710 off = fdt_node_offset_by_compatible(blob, off,
711 compat[i]);
712 }
713 }
714
715 return 0;
716}
717#endif
718
719int fdt_increase_size(void *fdt, int add_len)
720{
721 int newlen;
722
723 newlen = fdt_totalsize(fdt) + add_len;
724
725
726 return fdt_open_into(fdt, fdt, newlen);
727}
728
729#ifdef CONFIG_FDT_FIXUP_PARTITIONS
730#include <jffs2/load_kernel.h>
731#include <mtd_node.h>
732
733struct reg_cell {
734 unsigned int r0;
735 unsigned int r1;
736};
737
738int fdt_del_subnodes(const void *blob, int parent_offset)
739{
740 int off, ndepth;
741 int ret;
742
743 for (ndepth = 0, off = fdt_next_node(blob, parent_offset, &ndepth);
744 (off >= 0) && (ndepth > 0);
745 off = fdt_next_node(blob, off, &ndepth)) {
746 if (ndepth == 1) {
747 debug("delete %s: offset: %x\n",
748 fdt_get_name(blob, off, 0), off);
749 ret = fdt_del_node((void *)blob, off);
750 if (ret < 0) {
751 printf("Can't delete node: %s\n",
752 fdt_strerror(ret));
753 return ret;
754 } else {
755 ndepth = 0;
756 off = parent_offset;
757 }
758 }
759 }
760 return 0;
761}
762
763int fdt_del_partitions(void *blob, int parent_offset)
764{
765 const void *prop;
766 int ndepth = 0;
767 int off;
768 int ret;
769
770 off = fdt_next_node(blob, parent_offset, &ndepth);
771 if (off > 0 && ndepth == 1) {
772 prop = fdt_getprop(blob, off, "label", NULL);
773 if (prop == NULL) {
774
775
776
777
778 return fdt_del_partitions(blob, off);
779 } else {
780 ret = fdt_del_subnodes(blob, parent_offset);
781 if (ret < 0) {
782 printf("Can't remove subnodes: %s\n",
783 fdt_strerror(ret));
784 return ret;
785 }
786 }
787 }
788 return 0;
789}
790
791int fdt_node_set_part_info(void *blob, int parent_offset,
792 struct mtd_device *dev)
793{
794 struct list_head *pentry;
795 struct part_info *part;
796 struct reg_cell cell;
797 int off, ndepth = 0;
798 int part_num, ret;
799 char buf[64];
800
801 ret = fdt_del_partitions(blob, parent_offset);
802 if (ret < 0)
803 return ret;
804
805
806
807
808
809 off = fdt_next_node(blob, parent_offset, &ndepth);
810 if (off > 0 && ndepth == 1)
811 parent_offset = off;
812
813 part_num = 0;
814 list_for_each_prev(pentry, &dev->parts) {
815 int newoff;
816
817 part = list_entry(pentry, struct part_info, link);
818
819 debug("%2d: %-20s0x%08llx\t0x%08llx\t%d\n",
820 part_num, part->name, part->size,
821 part->offset, part->mask_flags);
822
823 sprintf(buf, "partition@%llx", part->offset);
824add_sub:
825 ret = fdt_add_subnode(blob, parent_offset, buf);
826 if (ret == -FDT_ERR_NOSPACE) {
827 ret = fdt_increase_size(blob, 512);
828 if (!ret)
829 goto add_sub;
830 else
831 goto err_size;
832 } else if (ret < 0) {
833 printf("Can't add partition node: %s\n",
834 fdt_strerror(ret));
835 return ret;
836 }
837 newoff = ret;
838
839
840 if (part->mask_flags & 1) {
841add_ro:
842 ret = fdt_setprop(blob, newoff, "read_only", NULL, 0);
843 if (ret == -FDT_ERR_NOSPACE) {
844 ret = fdt_increase_size(blob, 512);
845 if (!ret)
846 goto add_ro;
847 else
848 goto err_size;
849 } else if (ret < 0)
850 goto err_prop;
851 }
852
853 cell.r0 = cpu_to_fdt32(part->offset);
854 cell.r1 = cpu_to_fdt32(part->size);
855add_reg:
856 ret = fdt_setprop(blob, newoff, "reg", &cell, sizeof(cell));
857 if (ret == -FDT_ERR_NOSPACE) {
858 ret = fdt_increase_size(blob, 512);
859 if (!ret)
860 goto add_reg;
861 else
862 goto err_size;
863 } else if (ret < 0)
864 goto err_prop;
865
866add_label:
867 ret = fdt_setprop_string(blob, newoff, "label", part->name);
868 if (ret == -FDT_ERR_NOSPACE) {
869 ret = fdt_increase_size(blob, 512);
870 if (!ret)
871 goto add_label;
872 else
873 goto err_size;
874 } else if (ret < 0)
875 goto err_prop;
876
877 part_num++;
878 }
879 return 0;
880err_size:
881 printf("Can't increase blob size: %s\n", fdt_strerror(ret));
882 return ret;
883err_prop:
884 printf("Can't add property: %s\n", fdt_strerror(ret));
885 return ret;
886}
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902void fdt_fixup_mtdparts(void *blob, void *node_info, int node_info_size)
903{
904 struct node_info *ni = node_info;
905 struct mtd_device *dev;
906 char *parts;
907 int i, idx;
908 int noff;
909
910 parts = getenv("mtdparts");
911 if (!parts)
912 return;
913
914 if (mtdparts_init() != 0)
915 return;
916
917 for (i = 0; i < node_info_size; i++) {
918 idx = 0;
919 noff = fdt_node_offset_by_compatible(blob, -1, ni[i].compat);
920 while (noff != -FDT_ERR_NOTFOUND) {
921 debug("%s: %s, mtd dev type %d\n",
922 fdt_get_name(blob, noff, 0),
923 ni[i].compat, ni[i].type);
924 dev = device_find(ni[i].type, idx++);
925 if (dev) {
926 if (fdt_node_set_part_info(blob, noff, dev))
927 return;
928 }
929
930
931 noff = fdt_node_offset_by_compatible(blob, noff,
932 ni[i].compat);
933 }
934 }
935}
936#endif
937
938void fdt_del_node_and_alias(void *blob, const char *alias)
939{
940 int off = fdt_path_offset(blob, alias);
941
942 if (off < 0)
943 return;
944
945 fdt_del_node(blob, off);
946
947 off = fdt_path_offset(blob, "/aliases");
948 fdt_delprop(blob, off, alias);
949}
950
951
952#define OF_MAX_ADDR_CELLS 4
953#define OF_BAD_ADDR FDT_ADDR_T_NONE
954#define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \
955 (ns) > 0)
956
957
958#ifdef DEBUG
959static void of_dump_addr(const char *s, const fdt32_t *addr, int na)
960{
961 printf("%s", s);
962 while(na--)
963 printf(" %08x", *(addr++));
964 printf("\n");
965}
966#else
967static void of_dump_addr(const char *s, const fdt32_t *addr, int na) { }
968#endif
969
970
971
972
973
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
1000struct of_bus {
1001 const char *name;
1002 const char *addresses;
1003 int (*match)(const void *blob, int parentoffset);
1004 void (*count_cells)(const void *blob, int parentoffset,
1005 int *addrc, int *sizec);
1006 u64 (*map)(fdt32_t *addr, const fdt32_t *range,
1007 int na, int ns, int pna);
1008 int (*translate)(fdt32_t *addr, u64 offset, int na);
1009};
1010
1011
1012void of_bus_default_count_cells(const void *blob, int parentoffset,
1013 int *addrc, int *sizec)
1014{
1015 const fdt32_t *prop;
1016
1017 if (addrc)
1018 *addrc = fdt_address_cells(blob, parentoffset);
1019
1020 if (sizec) {
1021 prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL);
1022 if (prop)
1023 *sizec = be32_to_cpup(prop);
1024 else
1025 *sizec = 1;
1026 }
1027}
1028
1029static u64 of_bus_default_map(fdt32_t *addr, const fdt32_t *range,
1030 int na, int ns, int pna)
1031{
1032 u64 cp, s, da;
1033
1034 cp = of_read_number(range, na);
1035 s = of_read_number(range + na + pna, ns);
1036 da = of_read_number(addr, na);
1037
1038 debug("OF: default map, cp=%" PRIu64 ", s=%" PRIu64
1039 ", da=%" PRIu64 "\n", cp, s, da);
1040
1041 if (da < cp || da >= (cp + s))
1042 return OF_BAD_ADDR;
1043 return da - cp;
1044}
1045
1046static int of_bus_default_translate(fdt32_t *addr, u64 offset, int na)
1047{
1048 u64 a = of_read_number(addr, na);
1049 memset(addr, 0, na * 4);
1050 a += offset;
1051 if (na > 1)
1052 addr[na - 2] = cpu_to_fdt32(a >> 32);
1053 addr[na - 1] = cpu_to_fdt32(a & 0xffffffffu);
1054
1055 return 0;
1056}
1057
1058#ifdef CONFIG_OF_ISA_BUS
1059
1060
1061static int of_bus_isa_match(const void *blob, int parentoffset)
1062{
1063 const char *name;
1064
1065 name = fdt_get_name(blob, parentoffset, NULL);
1066 if (!name)
1067 return 0;
1068
1069 return !strcmp(name, "isa");
1070}
1071
1072static void of_bus_isa_count_cells(const void *blob, int parentoffset,
1073 int *addrc, int *sizec)
1074{
1075 if (addrc)
1076 *addrc = 2;
1077 if (sizec)
1078 *sizec = 1;
1079}
1080
1081static u64 of_bus_isa_map(fdt32_t *addr, const fdt32_t *range,
1082 int na, int ns, int pna)
1083{
1084 u64 cp, s, da;
1085
1086
1087 if ((addr[0] ^ range[0]) & cpu_to_be32(1))
1088 return OF_BAD_ADDR;
1089
1090 cp = of_read_number(range + 1, na - 1);
1091 s = of_read_number(range + na + pna, ns);
1092 da = of_read_number(addr + 1, na - 1);
1093
1094 debug("OF: ISA map, cp=%" PRIu64 ", s=%" PRIu64
1095 ", da=%" PRIu64 "\n", cp, s, da);
1096
1097 if (da < cp || da >= (cp + s))
1098 return OF_BAD_ADDR;
1099 return da - cp;
1100}
1101
1102static int of_bus_isa_translate(fdt32_t *addr, u64 offset, int na)
1103{
1104 return of_bus_default_translate(addr + 1, offset, na - 1);
1105}
1106
1107#endif
1108
1109
1110static struct of_bus of_busses[] = {
1111#ifdef CONFIG_OF_ISA_BUS
1112
1113 {
1114 .name = "isa",
1115 .addresses = "reg",
1116 .match = of_bus_isa_match,
1117 .count_cells = of_bus_isa_count_cells,
1118 .map = of_bus_isa_map,
1119 .translate = of_bus_isa_translate,
1120 },
1121#endif
1122
1123 {
1124 .name = "default",
1125 .addresses = "reg",
1126 .count_cells = of_bus_default_count_cells,
1127 .map = of_bus_default_map,
1128 .translate = of_bus_default_translate,
1129 },
1130};
1131
1132static struct of_bus *of_match_bus(const void *blob, int parentoffset)
1133{
1134 struct of_bus *bus;
1135
1136 if (ARRAY_SIZE(of_busses) == 1)
1137 return of_busses;
1138
1139 for (bus = of_busses; bus; bus++) {
1140 if (!bus->match || bus->match(blob, parentoffset))
1141 return bus;
1142 }
1143
1144
1145
1146
1147
1148
1149
1150 assert(0);
1151 return NULL;
1152}
1153
1154static int of_translate_one(const void *blob, int parent, struct of_bus *bus,
1155 struct of_bus *pbus, fdt32_t *addr,
1156 int na, int ns, int pna, const char *rprop)
1157{
1158 const fdt32_t *ranges;
1159 int rlen;
1160 int rone;
1161 u64 offset = OF_BAD_ADDR;
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175 ranges = fdt_getprop(blob, parent, rprop, &rlen);
1176 if (ranges == NULL || rlen == 0) {
1177 offset = of_read_number(addr, na);
1178 memset(addr, 0, pna * 4);
1179 debug("OF: no ranges, 1:1 translation\n");
1180 goto finish;
1181 }
1182
1183 debug("OF: walking ranges...\n");
1184
1185
1186 rlen /= 4;
1187 rone = na + pna + ns;
1188 for (; rlen >= rone; rlen -= rone, ranges += rone) {
1189 offset = bus->map(addr, ranges, na, ns, pna);
1190 if (offset != OF_BAD_ADDR)
1191 break;
1192 }
1193 if (offset == OF_BAD_ADDR) {
1194 debug("OF: not found !\n");
1195 return 1;
1196 }
1197 memcpy(addr, ranges + na, 4 * pna);
1198
1199 finish:
1200 of_dump_addr("OF: parent translation for:", addr, pna);
1201 debug("OF: with offset: %" PRIu64 "\n", offset);
1202
1203
1204 return pbus->translate(addr, offset, pna);
1205}
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217static u64 __of_translate_address(const void *blob, int node_offset,
1218 const fdt32_t *in_addr, const char *rprop)
1219{
1220 int parent;
1221 struct of_bus *bus, *pbus;
1222 fdt32_t addr[OF_MAX_ADDR_CELLS];
1223 int na, ns, pna, pns;
1224 u64 result = OF_BAD_ADDR;
1225
1226 debug("OF: ** translation for device %s **\n",
1227 fdt_get_name(blob, node_offset, NULL));
1228
1229
1230 parent = fdt_parent_offset(blob, node_offset);
1231 if (parent < 0)
1232 goto bail;
1233 bus = of_match_bus(blob, parent);
1234
1235
1236 bus->count_cells(blob, parent, &na, &ns);
1237 if (!OF_CHECK_COUNTS(na, ns)) {
1238 printf("%s: Bad cell count for %s\n", __FUNCTION__,
1239 fdt_get_name(blob, node_offset, NULL));
1240 goto bail;
1241 }
1242 memcpy(addr, in_addr, na * 4);
1243
1244 debug("OF: bus is %s (na=%d, ns=%d) on %s\n",
1245 bus->name, na, ns, fdt_get_name(blob, parent, NULL));
1246 of_dump_addr("OF: translating address:", addr, na);
1247
1248
1249 for (;;) {
1250
1251 node_offset = parent;
1252 parent = fdt_parent_offset(blob, node_offset);
1253
1254
1255 if (parent < 0) {
1256 debug("OF: reached root node\n");
1257 result = of_read_number(addr, na);
1258 break;
1259 }
1260
1261
1262 pbus = of_match_bus(blob, parent);
1263 pbus->count_cells(blob, parent, &pna, &pns);
1264 if (!OF_CHECK_COUNTS(pna, pns)) {
1265 printf("%s: Bad cell count for %s\n", __FUNCTION__,
1266 fdt_get_name(blob, node_offset, NULL));
1267 break;
1268 }
1269
1270 debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n",
1271 pbus->name, pna, pns, fdt_get_name(blob, parent, NULL));
1272
1273
1274 if (of_translate_one(blob, node_offset, bus, pbus,
1275 addr, na, ns, pna, rprop))
1276 break;
1277
1278
1279 na = pna;
1280 ns = pns;
1281 bus = pbus;
1282
1283 of_dump_addr("OF: one level translation:", addr, na);
1284 }
1285 bail:
1286
1287 return result;
1288}
1289
1290u64 fdt_translate_address(const void *blob, int node_offset,
1291 const fdt32_t *in_addr)
1292{
1293 return __of_translate_address(blob, node_offset, in_addr, "ranges");
1294}
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305int fdt_node_offset_by_compat_reg(void *blob, const char *compat,
1306 phys_addr_t compat_off)
1307{
1308 int len, off = fdt_node_offset_by_compatible(blob, -1, compat);
1309 while (off != -FDT_ERR_NOTFOUND) {
1310 const fdt32_t *reg = fdt_getprop(blob, off, "reg", &len);
1311 if (reg) {
1312 if (compat_off == fdt_translate_address(blob, off, reg))
1313 return off;
1314 }
1315 off = fdt_node_offset_by_compatible(blob, off, compat);
1316 }
1317
1318 return -FDT_ERR_NOTFOUND;
1319}
1320
1321
1322
1323
1324
1325
1326int fdt_alloc_phandle(void *blob)
1327{
1328 int offset;
1329 uint32_t phandle = 0;
1330
1331 for (offset = fdt_next_node(blob, -1, NULL); offset >= 0;
1332 offset = fdt_next_node(blob, offset, NULL)) {
1333 phandle = max(phandle, fdt_get_phandle(blob, offset));
1334 }
1335
1336 return phandle + 1;
1337}
1338
1339
1340
1341
1342
1343
1344
1345
1346int fdt_set_phandle(void *fdt, int nodeoffset, uint32_t phandle)
1347{
1348 int ret;
1349
1350#ifdef DEBUG
1351 int off = fdt_node_offset_by_phandle(fdt, phandle);
1352
1353 if ((off >= 0) && (off != nodeoffset)) {
1354 char buf[64];
1355
1356 fdt_get_path(fdt, nodeoffset, buf, sizeof(buf));
1357 printf("Trying to update node %s with phandle %u ",
1358 buf, phandle);
1359
1360 fdt_get_path(fdt, off, buf, sizeof(buf));
1361 printf("that already exists in node %s.\n", buf);
1362 return -FDT_ERR_BADPHANDLE;
1363 }
1364#endif
1365
1366 ret = fdt_setprop_cell(fdt, nodeoffset, "phandle", phandle);
1367 if (ret < 0)
1368 return ret;
1369
1370
1371
1372
1373
1374 ret = fdt_setprop_cell(fdt, nodeoffset, "linux,phandle", phandle);
1375
1376 return ret;
1377}
1378
1379
1380
1381
1382
1383
1384
1385unsigned int fdt_create_phandle(void *fdt, int nodeoffset)
1386{
1387
1388 int phandle = fdt_get_phandle(fdt, nodeoffset);
1389
1390
1391 if (phandle == 0) {
1392 int ret;
1393
1394 phandle = fdt_alloc_phandle(fdt);
1395 ret = fdt_set_phandle(fdt, nodeoffset, phandle);
1396 if (ret < 0) {
1397 printf("Can't set phandle %u: %s\n", phandle,
1398 fdt_strerror(ret));
1399 return 0;
1400 }
1401 }
1402
1403 return phandle;
1404}
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415int fdt_set_node_status(void *fdt, int nodeoffset,
1416 enum fdt_status status, unsigned int error_code)
1417{
1418 char buf[16];
1419 int ret = 0;
1420
1421 if (nodeoffset < 0)
1422 return nodeoffset;
1423
1424 switch (status) {
1425 case FDT_STATUS_OKAY:
1426 ret = fdt_setprop_string(fdt, nodeoffset, "status", "okay");
1427 break;
1428 case FDT_STATUS_DISABLED:
1429 ret = fdt_setprop_string(fdt, nodeoffset, "status", "disabled");
1430 break;
1431 case FDT_STATUS_FAIL:
1432 ret = fdt_setprop_string(fdt, nodeoffset, "status", "fail");
1433 break;
1434 case FDT_STATUS_FAIL_ERROR_CODE:
1435 sprintf(buf, "fail-%d", error_code);
1436 ret = fdt_setprop_string(fdt, nodeoffset, "status", buf);
1437 break;
1438 default:
1439 printf("Invalid fdt status: %x\n", status);
1440 ret = -1;
1441 break;
1442 }
1443
1444 return ret;
1445}
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456int fdt_set_status_by_alias(void *fdt, const char* alias,
1457 enum fdt_status status, unsigned int error_code)
1458{
1459 int offset = fdt_path_offset(fdt, alias);
1460
1461 return fdt_set_node_status(fdt, offset, status, error_code);
1462}
1463
1464#if defined(CONFIG_VIDEO) || defined(CONFIG_LCD)
1465int fdt_add_edid(void *blob, const char *compat, unsigned char *edid_buf)
1466{
1467 int noff;
1468 int ret;
1469
1470 noff = fdt_node_offset_by_compatible(blob, -1, compat);
1471 if (noff != -FDT_ERR_NOTFOUND) {
1472 debug("%s: %s\n", fdt_get_name(blob, noff, 0), compat);
1473add_edid:
1474 ret = fdt_setprop(blob, noff, "edid", edid_buf, 128);
1475 if (ret == -FDT_ERR_NOSPACE) {
1476 ret = fdt_increase_size(blob, 512);
1477 if (!ret)
1478 goto add_edid;
1479 else
1480 goto err_size;
1481 } else if (ret < 0) {
1482 printf("Can't add property: %s\n", fdt_strerror(ret));
1483 return ret;
1484 }
1485 }
1486 return 0;
1487err_size:
1488 printf("Can't increase blob size: %s\n", fdt_strerror(ret));
1489 return ret;
1490}
1491#endif
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502int fdt_verify_alias_address(void *fdt, int anode, const char *alias, u64 addr)
1503{
1504 const char *path;
1505 const fdt32_t *reg;
1506 int node, len;
1507 u64 dt_addr;
1508
1509 path = fdt_getprop(fdt, anode, alias, NULL);
1510 if (!path) {
1511
1512 return 1;
1513 }
1514
1515 node = fdt_path_offset(fdt, path);
1516 if (node < 0) {
1517 printf("Warning: device tree alias '%s' points to invalid "
1518 "node %s.\n", alias, path);
1519 return 0;
1520 }
1521
1522 reg = fdt_getprop(fdt, node, "reg", &len);
1523 if (!reg) {
1524 printf("Warning: device tree node '%s' has no address.\n",
1525 path);
1526 return 0;
1527 }
1528
1529 dt_addr = fdt_translate_address(fdt, node, reg);
1530 if (addr != dt_addr) {
1531 printf("Warning: U-Boot configured device %s at address %"
1532 PRIx64 ",\n but the device tree has it address %"
1533 PRIx64 ".\n", alias, addr, dt_addr);
1534 return 0;
1535 }
1536
1537 return 1;
1538}
1539
1540
1541
1542
1543u64 fdt_get_base_address(void *fdt, int node)
1544{
1545 int size;
1546 u32 naddr;
1547 const fdt32_t *prop;
1548
1549 naddr = fdt_address_cells(fdt, node);
1550
1551 prop = fdt_getprop(fdt, node, "ranges", &size);
1552
1553 return prop ? fdt_translate_address(fdt, node, prop + naddr) : 0;
1554}
1555
1556
1557
1558
1559static int fdt_read_prop(const fdt32_t *prop, int prop_len, int cell_off,
1560 uint64_t *val, int cells)
1561{
1562 const fdt32_t *prop32 = &prop[cell_off];
1563 const fdt64_t *prop64 = (const fdt64_t *)&prop[cell_off];
1564
1565 if ((cell_off + cells) > prop_len)
1566 return -FDT_ERR_NOSPACE;
1567
1568 switch (cells) {
1569 case 1:
1570 *val = fdt32_to_cpu(*prop32);
1571 break;
1572 case 2:
1573 *val = fdt64_to_cpu(*prop64);
1574 break;
1575 default:
1576 return -FDT_ERR_NOSPACE;
1577 }
1578
1579 return 0;
1580}
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595int fdt_read_range(void *fdt, int node, int n, uint64_t *child_addr,
1596 uint64_t *addr, uint64_t *len)
1597{
1598 int pnode = fdt_parent_offset(fdt, node);
1599 const fdt32_t *ranges;
1600 int pacells;
1601 int acells;
1602 int scells;
1603 int ranges_len;
1604 int cell = 0;
1605 int r = 0;
1606
1607
1608
1609
1610
1611
1612
1613 pacells = fdt_getprop_u32_default_node(fdt, pnode, 0, "#address-cells", 1);
1614 acells = fdt_getprop_u32_default_node(fdt, node, 0, "#address-cells", 1);
1615 scells = fdt_getprop_u32_default_node(fdt, node, 0, "#size-cells", 1);
1616
1617
1618 ranges = fdt_getprop(fdt, node, "ranges", &ranges_len);
1619 if (!ranges)
1620 return -FDT_ERR_NOTFOUND;
1621 ranges_len /= sizeof(uint32_t);
1622
1623
1624 cell = n * (pacells + acells + scells);
1625
1626
1627 if (child_addr) {
1628 r = fdt_read_prop(ranges, ranges_len, cell, child_addr,
1629 acells);
1630 if (r)
1631 return r;
1632 }
1633 cell += acells;
1634
1635
1636 if (addr)
1637 *addr = fdt_translate_address(fdt, node, ranges + cell);
1638 cell += pacells;
1639
1640
1641 if (len) {
1642 r = fdt_read_prop(ranges, ranges_len, cell, len, scells);
1643 if (r)
1644 return r;
1645 }
1646
1647 return 0;
1648}
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663int fdt_setup_simplefb_node(void *fdt, int node, u64 base_address, u32 width,
1664 u32 height, u32 stride, const char *format)
1665{
1666 char name[32];
1667 fdt32_t cells[4];
1668 int i, addrc, sizec, ret;
1669
1670 of_bus_default_count_cells(fdt, fdt_parent_offset(fdt, node),
1671 &addrc, &sizec);
1672 i = 0;
1673 if (addrc == 2)
1674 cells[i++] = cpu_to_fdt32(base_address >> 32);
1675 cells[i++] = cpu_to_fdt32(base_address);
1676 if (sizec == 2)
1677 cells[i++] = 0;
1678 cells[i++] = cpu_to_fdt32(height * stride);
1679
1680 ret = fdt_setprop(fdt, node, "reg", cells, sizeof(cells[0]) * i);
1681 if (ret < 0)
1682 return ret;
1683
1684 snprintf(name, sizeof(name), "framebuffer@%" PRIx64, base_address);
1685 ret = fdt_set_name(fdt, node, name);
1686 if (ret < 0)
1687 return ret;
1688
1689 ret = fdt_setprop_u32(fdt, node, "width", width);
1690 if (ret < 0)
1691 return ret;
1692
1693 ret = fdt_setprop_u32(fdt, node, "height", height);
1694 if (ret < 0)
1695 return ret;
1696
1697 ret = fdt_setprop_u32(fdt, node, "stride", stride);
1698 if (ret < 0)
1699 return ret;
1700
1701 ret = fdt_setprop_string(fdt, node, "format", format);
1702 if (ret < 0)
1703 return ret;
1704
1705 ret = fdt_setprop_string(fdt, node, "status", "okay");
1706 if (ret < 0)
1707 return ret;
1708
1709 return 0;
1710}
1711
1712
1713
1714
1715
1716int fdt_fixup_display(void *blob, const char *path, const char *display)
1717{
1718 int off, toff;
1719
1720 if (!display || !path)
1721 return -FDT_ERR_NOTFOUND;
1722
1723 toff = fdt_path_offset(blob, path);
1724 if (toff >= 0)
1725 toff = fdt_subnode_offset(blob, toff, "display-timings");
1726 if (toff < 0)
1727 return toff;
1728
1729 for (off = fdt_first_subnode(blob, toff);
1730 off >= 0;
1731 off = fdt_next_subnode(blob, off)) {
1732 uint32_t h = fdt_get_phandle(blob, off);
1733 debug("%s:0x%x\n", fdt_get_name(blob, off, NULL),
1734 fdt32_to_cpu(h));
1735 if (strcasecmp(fdt_get_name(blob, off, NULL), display) == 0)
1736 return fdt_setprop_u32(blob, toff, "native-mode", h);
1737 }
1738 return toff;
1739}
1740