3 * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
5 * See file CREDITS for list of people who contributed to this
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of
11 * the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
25 #include <stdio_dev.h>
26 #include <linux/ctype.h>
27 #include <linux/types.h>
28 #include <asm/global_data.h>
31 #include <fdt_support.h>
35 * Global data (for the gd->bd)
37 DECLARE_GLOBAL_DATA_PTR
;
40 * fdt_getprop_u32_default - Find a node and return it's property or a default
42 * @fdt: ptr to device tree
44 * @prop: property name
45 * @dflt: default value if the property isn't found
47 * Convenience function to find a node and return it's property or a
48 * default value if it doesn't exist.
50 u32
fdt_getprop_u32_default(void *fdt
, const char *path
, const char *prop
,
56 off
= fdt_path_offset(fdt
, path
);
60 val
= fdt_getprop(fdt
, off
, prop
, NULL
);
68 * fdt_find_and_setprop: Find a node and set it's property
70 * @fdt: ptr to device tree
72 * @prop: property name
73 * @val: ptr to new value
74 * @len: length of new property value
75 * @create: flag to create the property if it doesn't exist
77 * Convenience function to directly set a property given the path to the node.
79 int fdt_find_and_setprop(void *fdt
, const char *node
, const char *prop
,
80 const void *val
, int len
, int create
)
82 int nodeoff
= fdt_path_offset(fdt
, node
);
87 if ((!create
) && (fdt_get_property(fdt
, nodeoff
, prop
, 0) == NULL
))
88 return 0; /* create flag not set; so exit quietly */
90 return fdt_setprop(fdt
, nodeoff
, prop
, val
, len
);
93 #ifdef CONFIG_OF_STDOUT_VIA_ALIAS
95 #ifdef CONFIG_SERIAL_MULTI
96 static void fdt_fill_multisername(char *sername
, size_t maxlen
)
98 const char *outname
= stdio_devices
[stdout
]->name
;
100 if (strcmp(outname
, "serial") > 0)
101 strncpy(sername
, outname
, maxlen
);
104 if (strcmp(outname
+ 1, "serial") > 0)
105 strncpy(sername
, outname
+ 1, maxlen
);
108 static inline void fdt_fill_multisername(char *sername
, size_t maxlen
) {}
109 #endif /* CONFIG_SERIAL_MULTI */
111 static int fdt_fixup_stdout(void *fdt
, int chosenoff
)
114 #ifdef CONFIG_CONS_INDEX
116 char sername
[9] = { 0 };
119 fdt_fill_multisername(sername
, sizeof(sername
) - 1);
121 sprintf(sername
, "serial%d", CONFIG_CONS_INDEX
- 1);
123 err
= node
= fdt_path_offset(fdt
, "/aliases");
126 path
= fdt_getprop(fdt
, node
, sername
, &len
);
128 char *p
= malloc(len
);
129 err
= -FDT_ERR_NOSPACE
;
131 memcpy(p
, path
, len
);
132 err
= fdt_setprop(fdt
, chosenoff
,
133 "linux,stdout-path", p
, len
);
142 printf("WARNING: could not set linux,stdout-path %s.\n",
149 int fdt_initrd(void *fdt
, ulong initrd_start
, ulong initrd_end
, int force
)
157 /* Find the "chosen" node. */
158 nodeoffset
= fdt_path_offset (fdt
, "/chosen");
160 /* If there is no "chosen" node in the blob return */
161 if (nodeoffset
< 0) {
162 printf("fdt_initrd: %s\n", fdt_strerror(nodeoffset
));
166 /* just return if initrd_start/end aren't valid */
167 if ((initrd_start
== 0) || (initrd_end
== 0))
170 total
= fdt_num_mem_rsv(fdt
);
173 * Look for an existing entry and update it. If we don't find
174 * the entry, we will j be the next available slot.
176 for (j
= 0; j
< total
; j
++) {
177 err
= fdt_get_mem_rsv(fdt
, j
, &addr
, &size
);
178 if (addr
== initrd_start
) {
179 fdt_del_mem_rsv(fdt
, j
);
184 err
= fdt_add_mem_rsv(fdt
, initrd_start
, initrd_end
- initrd_start
+ 1);
186 printf("fdt_initrd: %s\n", fdt_strerror(err
));
190 path
= fdt_getprop(fdt
, nodeoffset
, "linux,initrd-start", NULL
);
191 if ((path
== NULL
) || force
) {
192 tmp
= __cpu_to_be32(initrd_start
);
193 err
= fdt_setprop(fdt
, nodeoffset
,
194 "linux,initrd-start", &tmp
, sizeof(tmp
));
197 "could not set linux,initrd-start %s.\n",
201 tmp
= __cpu_to_be32(initrd_end
);
202 err
= fdt_setprop(fdt
, nodeoffset
,
203 "linux,initrd-end", &tmp
, sizeof(tmp
));
205 printf("WARNING: could not set linux,initrd-end %s.\n",
215 int fdt_chosen(void *fdt
, int force
)
219 char *str
; /* used to set string properties */
222 err
= fdt_check_header(fdt
);
224 printf("fdt_chosen: %s\n", fdt_strerror(err
));
229 * Find the "chosen" node.
231 nodeoffset
= fdt_path_offset (fdt
, "/chosen");
234 * If there is no "chosen" node in the blob, create it.
236 if (nodeoffset
< 0) {
238 * Create a new node "/chosen" (offset 0 is root level)
240 nodeoffset
= fdt_add_subnode(fdt
, 0, "chosen");
241 if (nodeoffset
< 0) {
242 printf("WARNING: could not create /chosen %s.\n",
243 fdt_strerror(nodeoffset
));
249 * Create /chosen properites that don't exist in the fdt.
250 * If the property exists, update it only if the "force" parameter
253 str
= getenv("bootargs");
255 path
= fdt_getprop(fdt
, nodeoffset
, "bootargs", NULL
);
256 if ((path
== NULL
) || force
) {
257 err
= fdt_setprop(fdt
, nodeoffset
,
258 "bootargs", str
, strlen(str
)+1);
260 printf("WARNING: could not set bootargs %s.\n",
265 #ifdef CONFIG_OF_STDOUT_VIA_ALIAS
266 path
= fdt_getprop(fdt
, nodeoffset
, "linux,stdout-path", NULL
);
267 if ((path
== NULL
) || force
)
268 err
= fdt_fixup_stdout(fdt
, nodeoffset
);
271 #ifdef OF_STDOUT_PATH
272 path
= fdt_getprop(fdt
, nodeoffset
, "linux,stdout-path", NULL
);
273 if ((path
== NULL
) || force
) {
274 err
= fdt_setprop(fdt
, nodeoffset
,
275 "linux,stdout-path", OF_STDOUT_PATH
, strlen(OF_STDOUT_PATH
)+1);
277 printf("WARNING: could not set linux,stdout-path %s.\n",
285 void do_fixup_by_path(void *fdt
, const char *path
, const char *prop
,
286 const void *val
, int len
, int create
)
290 debug("Updating property '%s/%s' = ", path
, prop
);
291 for (i
= 0; i
< len
; i
++)
292 debug(" %.2x", *(u8
*)(val
+i
));
295 int rc
= fdt_find_and_setprop(fdt
, path
, prop
, val
, len
, create
);
297 printf("Unable to update property %s:%s, err=%s\n",
298 path
, prop
, fdt_strerror(rc
));
301 void do_fixup_by_path_u32(void *fdt
, const char *path
, const char *prop
,
304 val
= cpu_to_fdt32(val
);
305 do_fixup_by_path(fdt
, path
, prop
, &val
, sizeof(val
), create
);
308 void do_fixup_by_prop(void *fdt
,
309 const char *pname
, const void *pval
, int plen
,
310 const char *prop
, const void *val
, int len
,
316 debug("Updating property '%s' = ", prop
);
317 for (i
= 0; i
< len
; i
++)
318 debug(" %.2x", *(u8
*)(val
+i
));
321 off
= fdt_node_offset_by_prop_value(fdt
, -1, pname
, pval
, plen
);
322 while (off
!= -FDT_ERR_NOTFOUND
) {
323 if (create
|| (fdt_get_property(fdt
, off
, prop
, 0) != NULL
))
324 fdt_setprop(fdt
, off
, prop
, val
, len
);
325 off
= fdt_node_offset_by_prop_value(fdt
, off
, pname
, pval
, plen
);
329 void do_fixup_by_prop_u32(void *fdt
,
330 const char *pname
, const void *pval
, int plen
,
331 const char *prop
, u32 val
, int create
)
333 val
= cpu_to_fdt32(val
);
334 do_fixup_by_prop(fdt
, pname
, pval
, plen
, prop
, &val
, 4, create
);
337 void do_fixup_by_compat(void *fdt
, const char *compat
,
338 const char *prop
, const void *val
, int len
, int create
)
343 debug("Updating property '%s' = ", prop
);
344 for (i
= 0; i
< len
; i
++)
345 debug(" %.2x", *(u8
*)(val
+i
));
348 off
= fdt_node_offset_by_compatible(fdt
, -1, compat
);
349 while (off
!= -FDT_ERR_NOTFOUND
) {
350 if (create
|| (fdt_get_property(fdt
, off
, prop
, 0) != NULL
))
351 fdt_setprop(fdt
, off
, prop
, val
, len
);
352 off
= fdt_node_offset_by_compatible(fdt
, off
, compat
);
356 void do_fixup_by_compat_u32(void *fdt
, const char *compat
,
357 const char *prop
, u32 val
, int create
)
359 val
= cpu_to_fdt32(val
);
360 do_fixup_by_compat(fdt
, compat
, prop
, &val
, 4, create
);
363 int fdt_fixup_memory(void *blob
, u64 start
, u64 size
)
365 int err
, nodeoffset
, len
= 0;
367 const u32
*addrcell
, *sizecell
;
369 err
= fdt_check_header(blob
);
371 printf("%s: %s\n", __FUNCTION__
, fdt_strerror(err
));
375 /* update, or add and update /memory node */
376 nodeoffset
= fdt_path_offset(blob
, "/memory");
377 if (nodeoffset
< 0) {
378 nodeoffset
= fdt_add_subnode(blob
, 0, "memory");
380 printf("WARNING: could not create /memory: %s.\n",
381 fdt_strerror(nodeoffset
));
384 err
= fdt_setprop(blob
, nodeoffset
, "device_type", "memory",
387 printf("WARNING: could not set %s %s.\n", "device_type",
392 addrcell
= fdt_getprop(blob
, 0, "#address-cells", NULL
);
393 /* use shifts and mask to ensure endianness */
394 if ((addrcell
) && (*addrcell
== 2)) {
395 tmp
[0] = (start
>> 56) & 0xff;
396 tmp
[1] = (start
>> 48) & 0xff;
397 tmp
[2] = (start
>> 40) & 0xff;
398 tmp
[3] = (start
>> 32) & 0xff;
399 tmp
[4] = (start
>> 24) & 0xff;
400 tmp
[5] = (start
>> 16) & 0xff;
401 tmp
[6] = (start
>> 8) & 0xff;
402 tmp
[7] = (start
) & 0xff;
405 tmp
[0] = (start
>> 24) & 0xff;
406 tmp
[1] = (start
>> 16) & 0xff;
407 tmp
[2] = (start
>> 8) & 0xff;
408 tmp
[3] = (start
) & 0xff;
412 sizecell
= fdt_getprop(blob
, 0, "#size-cells", NULL
);
413 /* use shifts and mask to ensure endianness */
414 if ((sizecell
) && (*sizecell
== 2)) {
415 tmp
[0+len
] = (size
>> 56) & 0xff;
416 tmp
[1+len
] = (size
>> 48) & 0xff;
417 tmp
[2+len
] = (size
>> 40) & 0xff;
418 tmp
[3+len
] = (size
>> 32) & 0xff;
419 tmp
[4+len
] = (size
>> 24) & 0xff;
420 tmp
[5+len
] = (size
>> 16) & 0xff;
421 tmp
[6+len
] = (size
>> 8) & 0xff;
422 tmp
[7+len
] = (size
) & 0xff;
425 tmp
[0+len
] = (size
>> 24) & 0xff;
426 tmp
[1+len
] = (size
>> 16) & 0xff;
427 tmp
[2+len
] = (size
>> 8) & 0xff;
428 tmp
[3+len
] = (size
) & 0xff;
432 err
= fdt_setprop(blob
, nodeoffset
, "reg", tmp
, len
);
434 printf("WARNING: could not set %s %s.\n",
435 "reg", fdt_strerror(err
));
441 void fdt_fixup_ethernet(void *fdt
)
444 char enet
[16], *tmp
, *end
;
445 char mac
[16] = "ethaddr";
447 unsigned char mac_addr
[6];
449 node
= fdt_path_offset(fdt
, "/aliases");
454 while ((tmp
= getenv(mac
)) != NULL
) {
455 sprintf(enet
, "ethernet%d", i
);
456 path
= fdt_getprop(fdt
, node
, enet
, NULL
);
458 debug("No alias for %s\n", enet
);
459 sprintf(mac
, "eth%daddr", ++i
);
463 for (j
= 0; j
< 6; j
++) {
464 mac_addr
[j
] = tmp
? simple_strtoul(tmp
, &end
, 16) : 0;
466 tmp
= (*end
) ? end
+1 : end
;
469 do_fixup_by_path(fdt
, path
, "mac-address", &mac_addr
, 6, 0);
470 do_fixup_by_path(fdt
, path
, "local-mac-address",
473 sprintf(mac
, "eth%daddr", ++i
);
477 #ifdef CONFIG_HAS_FSL_DR_USB
478 void fdt_fixup_dr_usb(void *blob
, bd_t
*bd
)
482 const char *compat
= "fsl-usb2-dr";
483 const char *prop_mode
= "dr_mode";
484 const char *prop_type
= "phy_type";
488 mode
= getenv("usb_dr_mode");
489 type
= getenv("usb_phy_type");
493 node_offset
= fdt_node_offset_by_compatible(blob
, 0, compat
);
494 if (node_offset
< 0) {
495 printf("WARNING: could not find compatible node %s: %s.\n",
496 compat
, fdt_strerror(node_offset
));
501 err
= fdt_setprop(blob
, node_offset
, prop_mode
, mode
,
504 printf("WARNING: could not set %s for %s: %s.\n",
505 prop_mode
, compat
, fdt_strerror(err
));
509 err
= fdt_setprop(blob
, node_offset
, prop_type
, type
,
512 printf("WARNING: could not set %s for %s: %s.\n",
513 prop_type
, compat
, fdt_strerror(err
));
516 #endif /* CONFIG_HAS_FSL_DR_USB */
518 #if defined(CONFIG_MPC83xx) || defined(CONFIG_MPC85xx)
520 * update crypto node properties to a specified revision of the SEC
521 * called with sec_rev == 0 if not on an mpc8xxxE processor
523 void fdt_fixup_crypto_node(void *blob
, int sec_rev
)
525 const struct sec_rev_prop
{
528 u32 channel_fifo_len
;
530 u32 descriptor_types_mask
;
531 } sec_rev_prop_list
[] = {
532 { 0x0200, 4, 24, 0x07e, 0x01010ebf }, /* SEC 2.0 */
533 { 0x0201, 4, 24, 0x0fe, 0x012b0ebf }, /* SEC 2.1 */
534 { 0x0202, 1, 24, 0x04c, 0x0122003f }, /* SEC 2.2 */
535 { 0x0204, 4, 24, 0x07e, 0x012b0ebf }, /* SEC 2.4 */
536 { 0x0300, 4, 24, 0x9fe, 0x03ab0ebf }, /* SEC 3.0 */
537 { 0x0303, 4, 24, 0x97c, 0x03ab0abf }, /* SEC 3.3 */
539 char compat_strlist
[ARRAY_SIZE(sec_rev_prop_list
) *
540 sizeof("fsl,secX.Y")];
541 int crypto_node
, sec_idx
, err
;
545 /* locate crypto node based on lowest common compatible */
546 crypto_node
= fdt_node_offset_by_compatible(blob
, -1, "fsl,sec2.0");
547 if (crypto_node
== -FDT_ERR_NOTFOUND
)
550 /* delete it if not on an E-processor */
551 if (crypto_node
> 0 && !sec_rev
) {
552 fdt_del_node(blob
, crypto_node
);
556 /* else we got called for possible uprev */
557 for (sec_idx
= 0; sec_idx
< ARRAY_SIZE(sec_rev_prop_list
); sec_idx
++)
558 if (sec_rev_prop_list
[sec_idx
].sec_rev
== sec_rev
)
561 if (sec_idx
== ARRAY_SIZE(sec_rev_prop_list
)) {
562 puts("warning: unknown SEC revision number\n");
566 val
= cpu_to_fdt32(sec_rev_prop_list
[sec_idx
].num_channels
);
567 err
= fdt_setprop(blob
, crypto_node
, "fsl,num-channels", &val
, 4);
569 printf("WARNING: could not set crypto property: %s\n",
572 val
= cpu_to_fdt32(sec_rev_prop_list
[sec_idx
].descriptor_types_mask
);
573 err
= fdt_setprop(blob
, crypto_node
, "fsl,descriptor-types-mask", &val
, 4);
575 printf("WARNING: could not set crypto property: %s\n",
578 val
= cpu_to_fdt32(sec_rev_prop_list
[sec_idx
].exec_units_mask
);
579 err
= fdt_setprop(blob
, crypto_node
, "fsl,exec-units-mask", &val
, 4);
581 printf("WARNING: could not set crypto property: %s\n",
584 val
= cpu_to_fdt32(sec_rev_prop_list
[sec_idx
].channel_fifo_len
);
585 err
= fdt_setprop(blob
, crypto_node
, "fsl,channel-fifo-len", &val
, 4);
587 printf("WARNING: could not set crypto property: %s\n",
591 while (sec_idx
>= 0) {
592 p
= compat_strlist
+ val
;
593 val
+= sprintf(p
, "fsl,sec%d.%d",
594 (sec_rev_prop_list
[sec_idx
].sec_rev
& 0xff00) >> 8,
595 sec_rev_prop_list
[sec_idx
].sec_rev
& 0x00ff) + 1;
598 err
= fdt_setprop(blob
, crypto_node
, "compatible", &compat_strlist
, val
);
600 printf("WARNING: could not set crypto property: %s\n",
603 #endif /* defined(CONFIG_MPC83xx) || defined(CONFIG_MPC85xx) */
605 /* Resize the fdt to its actual size + a bit of padding */
606 int fdt_resize(void *blob
)
616 total
= fdt_num_mem_rsv(blob
);
617 for (i
= 0; i
< total
; i
++) {
618 fdt_get_mem_rsv(blob
, i
, &addr
, &size
);
619 if (addr
== (uint64_t)(u32
)blob
) {
620 fdt_del_mem_rsv(blob
, i
);
626 * Calculate the actual size of the fdt
627 * plus the size needed for two fdt_add_mem_rsv, one
628 * for the fdt itself and one for a possible initrd
630 actualsize
= fdt_off_dt_strings(blob
) +
631 fdt_size_dt_strings(blob
) + 2*sizeof(struct fdt_reserve_entry
);
633 /* Make it so the fdt ends on a page boundary */
634 actualsize
= ALIGN(actualsize
+ ((uint
)blob
& 0xfff), 0x1000);
635 actualsize
= actualsize
- ((uint
)blob
& 0xfff);
637 /* Change the fdt header to reflect the correct size */
638 fdt_set_totalsize(blob
, actualsize
);
640 /* Add the new reservation */
641 ret
= fdt_add_mem_rsv(blob
, (uint
)blob
, actualsize
);
649 #define CONFIG_SYS_PCI_NR_INBOUND_WIN 4
651 #define FDT_PCI_PREFETCH (0x40000000)
652 #define FDT_PCI_MEM32 (0x02000000)
653 #define FDT_PCI_IO (0x01000000)
654 #define FDT_PCI_MEM64 (0x03000000)
656 int fdt_pci_dma_ranges(void *blob
, int phb_off
, struct pci_controller
*hose
) {
658 int addrcell
, sizecell
, len
, r
;
660 /* sized based on pci addr cells, size-cells, & address-cells */
661 u32 dma_ranges
[(3 + 2 + 2) * CONFIG_SYS_PCI_NR_INBOUND_WIN
];
663 addrcell
= fdt_getprop_u32_default(blob
, "/", "#address-cells", 1);
664 sizecell
= fdt_getprop_u32_default(blob
, "/", "#size-cells", 1);
666 dma_range
= &dma_ranges
[0];
667 for (r
= 0; r
< hose
->region_count
; r
++) {
668 u64 bus_start
, phys_start
, size
;
670 /* skip if !PCI_REGION_SYS_MEMORY */
671 if (!(hose
->regions
[r
].flags
& PCI_REGION_SYS_MEMORY
))
674 bus_start
= (u64
)hose
->regions
[r
].bus_start
;
675 phys_start
= (u64
)hose
->regions
[r
].phys_start
;
676 size
= (u64
)hose
->regions
[r
].size
;
679 if (size
>= 0x100000000ull
)
680 dma_range
[0] |= FDT_PCI_MEM64
;
682 dma_range
[0] |= FDT_PCI_MEM32
;
683 if (hose
->regions
[r
].flags
& PCI_REGION_PREFETCH
)
684 dma_range
[0] |= FDT_PCI_PREFETCH
;
685 #ifdef CONFIG_SYS_PCI_64BIT
686 dma_range
[1] = bus_start
>> 32;
690 dma_range
[2] = bus_start
& 0xffffffff;
693 dma_range
[3] = phys_start
>> 32;
694 dma_range
[4] = phys_start
& 0xffffffff;
696 dma_range
[3] = phys_start
& 0xffffffff;
700 dma_range
[3 + addrcell
+ 0] = size
>> 32;
701 dma_range
[3 + addrcell
+ 1] = size
& 0xffffffff;
703 dma_range
[3 + addrcell
+ 0] = size
& 0xffffffff;
706 dma_range
+= (3 + addrcell
+ sizecell
);
709 len
= dma_range
- &dma_ranges
[0];
711 fdt_setprop(blob
, phb_off
, "dma-ranges", &dma_ranges
[0], len
*4);
717 #ifdef CONFIG_FDT_FIXUP_NOR_FLASH_SIZE
719 * This function can be used to update the size in the "reg" property
720 * of the NOR FLASH device nodes. This is necessary for boards with
721 * non-fixed NOR FLASH sizes.
723 int fdt_fixup_nor_flash_size(void *blob
, int cs
, u32 size
)
725 char compat
[][16] = { "cfi-flash", "jedec-flash" };
728 struct fdt_property
*prop
;
732 for (i
= 0; i
< 2; i
++) {
733 off
= fdt_node_offset_by_compatible(blob
, -1, compat
[i
]);
734 while (off
!= -FDT_ERR_NOTFOUND
) {
736 * Found one compatible node, now check if this one
739 prop
= fdt_get_property_w(blob
, off
, "reg", &len
);
741 reg
= (u32
*)&prop
->data
[0];
744 fdt_setprop(blob
, off
, "reg", reg
,
751 /* Move to next compatible node */
752 off
= fdt_node_offset_by_compatible(blob
, off
,