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 <linux/ctype.h>
26 #include <linux/types.h>
27 #include <asm/global_data.h>
30 #include <fdt_support.h>
34 * Global data (for the gd->bd)
36 DECLARE_GLOBAL_DATA_PTR
;
39 * fdt_getprop_u32_default - Find a node and return it's property or a default
41 * @fdt: ptr to device tree
43 * @prop: property name
44 * @dflt: default value if the property isn't found
46 * Convenience function to find a node and return it's property or a
47 * default value if it doesn't exist.
49 u32
fdt_getprop_u32_default(void *fdt
, const char *path
, const char *prop
,
55 off
= fdt_path_offset(fdt
, path
);
59 val
= fdt_getprop(fdt
, off
, prop
, NULL
);
67 * fdt_find_and_setprop: Find a node and set it's property
69 * @fdt: ptr to device tree
71 * @prop: property name
72 * @val: ptr to new value
73 * @len: length of new property value
74 * @create: flag to create the property if it doesn't exist
76 * Convenience function to directly set a property given the path to the node.
78 int fdt_find_and_setprop(void *fdt
, const char *node
, const char *prop
,
79 const void *val
, int len
, int create
)
81 int nodeoff
= fdt_path_offset(fdt
, node
);
86 if ((!create
) && (fdt_get_property(fdt
, nodeoff
, prop
, 0) == NULL
))
87 return 0; /* create flag not set; so exit quietly */
89 return fdt_setprop(fdt
, nodeoff
, prop
, val
, len
);
92 #ifdef CONFIG_OF_STDOUT_VIA_ALIAS
93 static int fdt_fixup_stdout(void *fdt
, int chosenoff
)
96 #ifdef CONFIG_CONS_INDEX
98 char sername
[9] = { 0 };
101 sprintf(sername
, "serial%d", CONFIG_CONS_INDEX
- 1);
103 err
= node
= fdt_path_offset(fdt
, "/aliases");
106 path
= fdt_getprop(fdt
, node
, sername
, &len
);
108 char *p
= malloc(len
);
109 err
= -FDT_ERR_NOSPACE
;
111 memcpy(p
, path
, len
);
112 err
= fdt_setprop(fdt
, chosenoff
,
113 "linux,stdout-path", p
, len
);
122 printf("WARNING: could not set linux,stdout-path %s.\n",
129 int fdt_initrd(void *fdt
, ulong initrd_start
, ulong initrd_end
, int force
)
137 /* Find the "chosen" node. */
138 nodeoffset
= fdt_path_offset (fdt
, "/chosen");
140 /* If there is no "chosen" node in the blob return */
141 if (nodeoffset
< 0) {
142 printf("fdt_initrd: %s\n", fdt_strerror(nodeoffset
));
146 /* just return if initrd_start/end aren't valid */
147 if ((initrd_start
== 0) || (initrd_end
== 0))
150 total
= fdt_num_mem_rsv(fdt
);
153 * Look for an existing entry and update it. If we don't find
154 * the entry, we will j be the next available slot.
156 for (j
= 0; j
< total
; j
++) {
157 err
= fdt_get_mem_rsv(fdt
, j
, &addr
, &size
);
158 if (addr
== initrd_start
) {
159 fdt_del_mem_rsv(fdt
, j
);
164 err
= fdt_add_mem_rsv(fdt
, initrd_start
, initrd_end
- initrd_start
+ 1);
166 printf("fdt_initrd: %s\n", fdt_strerror(err
));
170 path
= fdt_getprop(fdt
, nodeoffset
, "linux,initrd-start", NULL
);
171 if ((path
== NULL
) || force
) {
172 tmp
= __cpu_to_be32(initrd_start
);
173 err
= fdt_setprop(fdt
, nodeoffset
,
174 "linux,initrd-start", &tmp
, sizeof(tmp
));
177 "could not set linux,initrd-start %s.\n",
181 tmp
= __cpu_to_be32(initrd_end
);
182 err
= fdt_setprop(fdt
, nodeoffset
,
183 "linux,initrd-end", &tmp
, sizeof(tmp
));
185 printf("WARNING: could not set linux,initrd-end %s.\n",
195 int fdt_chosen(void *fdt
, int force
)
199 char *str
; /* used to set string properties */
202 err
= fdt_check_header(fdt
);
204 printf("fdt_chosen: %s\n", fdt_strerror(err
));
209 * Find the "chosen" node.
211 nodeoffset
= fdt_path_offset (fdt
, "/chosen");
214 * If there is no "chosen" node in the blob, create it.
216 if (nodeoffset
< 0) {
218 * Create a new node "/chosen" (offset 0 is root level)
220 nodeoffset
= fdt_add_subnode(fdt
, 0, "chosen");
221 if (nodeoffset
< 0) {
222 printf("WARNING: could not create /chosen %s.\n",
223 fdt_strerror(nodeoffset
));
229 * Create /chosen properites that don't exist in the fdt.
230 * If the property exists, update it only if the "force" parameter
233 str
= getenv("bootargs");
235 path
= fdt_getprop(fdt
, nodeoffset
, "bootargs", NULL
);
236 if ((path
== NULL
) || force
) {
237 err
= fdt_setprop(fdt
, nodeoffset
,
238 "bootargs", str
, strlen(str
)+1);
240 printf("WARNING: could not set bootargs %s.\n",
245 #ifdef CONFIG_OF_STDOUT_VIA_ALIAS
246 path
= fdt_getprop(fdt
, nodeoffset
, "linux,stdout-path", NULL
);
247 if ((path
== NULL
) || force
)
248 err
= fdt_fixup_stdout(fdt
, nodeoffset
);
251 #ifdef OF_STDOUT_PATH
252 path
= fdt_getprop(fdt
, nodeoffset
, "linux,stdout-path", NULL
);
253 if ((path
== NULL
) || force
) {
254 err
= fdt_setprop(fdt
, nodeoffset
,
255 "linux,stdout-path", OF_STDOUT_PATH
, strlen(OF_STDOUT_PATH
)+1);
257 printf("WARNING: could not set linux,stdout-path %s.\n",
265 void do_fixup_by_path(void *fdt
, const char *path
, const char *prop
,
266 const void *val
, int len
, int create
)
270 debug("Updating property '%s/%s' = ", path
, prop
);
271 for (i
= 0; i
< len
; i
++)
272 debug(" %.2x", *(u8
*)(val
+i
));
275 int rc
= fdt_find_and_setprop(fdt
, path
, prop
, val
, len
, create
);
277 printf("Unable to update property %s:%s, err=%s\n",
278 path
, prop
, fdt_strerror(rc
));
281 void do_fixup_by_path_u32(void *fdt
, const char *path
, const char *prop
,
284 val
= cpu_to_fdt32(val
);
285 do_fixup_by_path(fdt
, path
, prop
, &val
, sizeof(val
), create
);
288 void do_fixup_by_prop(void *fdt
,
289 const char *pname
, const void *pval
, int plen
,
290 const char *prop
, const void *val
, int len
,
296 debug("Updating property '%s' = ", prop
);
297 for (i
= 0; i
< len
; i
++)
298 debug(" %.2x", *(u8
*)(val
+i
));
301 off
= fdt_node_offset_by_prop_value(fdt
, -1, pname
, pval
, plen
);
302 while (off
!= -FDT_ERR_NOTFOUND
) {
303 if (create
|| (fdt_get_property(fdt
, off
, prop
, 0) != NULL
))
304 fdt_setprop(fdt
, off
, prop
, val
, len
);
305 off
= fdt_node_offset_by_prop_value(fdt
, off
, pname
, pval
, plen
);
309 void do_fixup_by_prop_u32(void *fdt
,
310 const char *pname
, const void *pval
, int plen
,
311 const char *prop
, u32 val
, int create
)
313 val
= cpu_to_fdt32(val
);
314 do_fixup_by_prop(fdt
, pname
, pval
, plen
, prop
, &val
, 4, create
);
317 void do_fixup_by_compat(void *fdt
, const char *compat
,
318 const char *prop
, const void *val
, int len
, int create
)
323 debug("Updating property '%s' = ", prop
);
324 for (i
= 0; i
< len
; i
++)
325 debug(" %.2x", *(u8
*)(val
+i
));
328 off
= fdt_node_offset_by_compatible(fdt
, -1, compat
);
329 while (off
!= -FDT_ERR_NOTFOUND
) {
330 if (create
|| (fdt_get_property(fdt
, off
, prop
, 0) != NULL
))
331 fdt_setprop(fdt
, off
, prop
, val
, len
);
332 off
= fdt_node_offset_by_compatible(fdt
, off
, compat
);
336 void do_fixup_by_compat_u32(void *fdt
, const char *compat
,
337 const char *prop
, u32 val
, int create
)
339 val
= cpu_to_fdt32(val
);
340 do_fixup_by_compat(fdt
, compat
, prop
, &val
, 4, create
);
343 int fdt_fixup_memory(void *blob
, u64 start
, u64 size
)
345 int err
, nodeoffset
, len
= 0;
347 const u32
*addrcell
, *sizecell
;
349 err
= fdt_check_header(blob
);
351 printf("%s: %s\n", __FUNCTION__
, fdt_strerror(err
));
355 /* update, or add and update /memory node */
356 nodeoffset
= fdt_path_offset(blob
, "/memory");
357 if (nodeoffset
< 0) {
358 nodeoffset
= fdt_add_subnode(blob
, 0, "memory");
360 printf("WARNING: could not create /memory: %s.\n",
361 fdt_strerror(nodeoffset
));
364 err
= fdt_setprop(blob
, nodeoffset
, "device_type", "memory",
367 printf("WARNING: could not set %s %s.\n", "device_type",
372 addrcell
= fdt_getprop(blob
, 0, "#address-cells", NULL
);
373 /* use shifts and mask to ensure endianness */
374 if ((addrcell
) && (*addrcell
== 2)) {
375 tmp
[0] = (start
>> 56) & 0xff;
376 tmp
[1] = (start
>> 48) & 0xff;
377 tmp
[2] = (start
>> 40) & 0xff;
378 tmp
[3] = (start
>> 32) & 0xff;
379 tmp
[4] = (start
>> 24) & 0xff;
380 tmp
[5] = (start
>> 16) & 0xff;
381 tmp
[6] = (start
>> 8) & 0xff;
382 tmp
[7] = (start
) & 0xff;
385 tmp
[0] = (start
>> 24) & 0xff;
386 tmp
[1] = (start
>> 16) & 0xff;
387 tmp
[2] = (start
>> 8) & 0xff;
388 tmp
[3] = (start
) & 0xff;
392 sizecell
= fdt_getprop(blob
, 0, "#size-cells", NULL
);
393 /* use shifts and mask to ensure endianness */
394 if ((sizecell
) && (*sizecell
== 2)) {
395 tmp
[0+len
] = (size
>> 56) & 0xff;
396 tmp
[1+len
] = (size
>> 48) & 0xff;
397 tmp
[2+len
] = (size
>> 40) & 0xff;
398 tmp
[3+len
] = (size
>> 32) & 0xff;
399 tmp
[4+len
] = (size
>> 24) & 0xff;
400 tmp
[5+len
] = (size
>> 16) & 0xff;
401 tmp
[6+len
] = (size
>> 8) & 0xff;
402 tmp
[7+len
] = (size
) & 0xff;
405 tmp
[0+len
] = (size
>> 24) & 0xff;
406 tmp
[1+len
] = (size
>> 16) & 0xff;
407 tmp
[2+len
] = (size
>> 8) & 0xff;
408 tmp
[3+len
] = (size
) & 0xff;
412 err
= fdt_setprop(blob
, nodeoffset
, "reg", tmp
, len
);
414 printf("WARNING: could not set %s %s.\n",
415 "reg", fdt_strerror(err
));
421 void fdt_fixup_ethernet(void *fdt
)
424 char enet
[16], *tmp
, *end
;
425 char mac
[16] = "ethaddr";
427 unsigned char mac_addr
[6];
429 node
= fdt_path_offset(fdt
, "/aliases");
434 while ((tmp
= getenv(mac
)) != NULL
) {
435 sprintf(enet
, "ethernet%d", i
);
436 path
= fdt_getprop(fdt
, node
, enet
, NULL
);
438 debug("No alias for %s\n", enet
);
439 sprintf(mac
, "eth%daddr", ++i
);
443 for (j
= 0; j
< 6; j
++) {
444 mac_addr
[j
] = tmp
? simple_strtoul(tmp
, &end
, 16) : 0;
446 tmp
= (*end
) ? end
+1 : end
;
449 do_fixup_by_path(fdt
, path
, "mac-address", &mac_addr
, 6, 0);
450 do_fixup_by_path(fdt
, path
, "local-mac-address",
453 sprintf(mac
, "eth%daddr", ++i
);
457 #ifdef CONFIG_HAS_FSL_DR_USB
458 void fdt_fixup_dr_usb(void *blob
, bd_t
*bd
)
462 const char *compat
= "fsl-usb2-dr";
463 const char *prop_mode
= "dr_mode";
464 const char *prop_type
= "phy_type";
468 mode
= getenv("usb_dr_mode");
469 type
= getenv("usb_phy_type");
473 node_offset
= fdt_node_offset_by_compatible(blob
, 0, compat
);
474 if (node_offset
< 0) {
475 printf("WARNING: could not find compatible node %s: %s.\n",
476 compat
, fdt_strerror(node_offset
));
481 err
= fdt_setprop(blob
, node_offset
, prop_mode
, mode
,
484 printf("WARNING: could not set %s for %s: %s.\n",
485 prop_mode
, compat
, fdt_strerror(err
));
489 err
= fdt_setprop(blob
, node_offset
, prop_type
, type
,
492 printf("WARNING: could not set %s for %s: %s.\n",
493 prop_type
, compat
, fdt_strerror(err
));
496 #endif /* CONFIG_HAS_FSL_DR_USB */
498 #if defined(CONFIG_MPC83xx) || defined(CONFIG_MPC85xx)
500 * update crypto node properties to a specified revision of the SEC
501 * called with sec_rev == 0 if not on an mpc8xxxE processor
503 void fdt_fixup_crypto_node(void *blob
, int sec_rev
)
505 const struct sec_rev_prop
{
508 u32 channel_fifo_len
;
510 u32 descriptor_types_mask
;
511 } sec_rev_prop_list
[] = {
512 { 0x0200, 4, 24, 0x07e, 0x01010ebf }, /* SEC 2.0 */
513 { 0x0201, 4, 24, 0x0fe, 0x012b0ebf }, /* SEC 2.1 */
514 { 0x0202, 1, 24, 0x04c, 0x0122003f }, /* SEC 2.2 */
515 { 0x0204, 4, 24, 0x07e, 0x012b0ebf }, /* SEC 2.4 */
516 { 0x0300, 4, 24, 0x9fe, 0x03ab0ebf }, /* SEC 3.0 */
517 { 0x0303, 4, 24, 0x97c, 0x03ab0abf }, /* SEC 3.3 */
519 char compat_strlist
[ARRAY_SIZE(sec_rev_prop_list
) *
520 sizeof("fsl,secX.Y")];
521 int crypto_node
, sec_idx
, err
;
525 /* locate crypto node based on lowest common compatible */
526 crypto_node
= fdt_node_offset_by_compatible(blob
, -1, "fsl,sec2.0");
527 if (crypto_node
== -FDT_ERR_NOTFOUND
)
530 /* delete it if not on an E-processor */
531 if (crypto_node
> 0 && !sec_rev
) {
532 fdt_del_node(blob
, crypto_node
);
536 /* else we got called for possible uprev */
537 for (sec_idx
= 0; sec_idx
< ARRAY_SIZE(sec_rev_prop_list
); sec_idx
++)
538 if (sec_rev_prop_list
[sec_idx
].sec_rev
== sec_rev
)
541 if (sec_idx
== ARRAY_SIZE(sec_rev_prop_list
)) {
542 puts("warning: unknown SEC revision number\n");
546 val
= cpu_to_fdt32(sec_rev_prop_list
[sec_idx
].num_channels
);
547 err
= fdt_setprop(blob
, crypto_node
, "fsl,num-channels", &val
, 4);
549 printf("WARNING: could not set crypto property: %s\n",
552 val
= cpu_to_fdt32(sec_rev_prop_list
[sec_idx
].descriptor_types_mask
);
553 err
= fdt_setprop(blob
, crypto_node
, "fsl,descriptor-types-mask", &val
, 4);
555 printf("WARNING: could not set crypto property: %s\n",
558 val
= cpu_to_fdt32(sec_rev_prop_list
[sec_idx
].exec_units_mask
);
559 err
= fdt_setprop(blob
, crypto_node
, "fsl,exec-units-mask", &val
, 4);
561 printf("WARNING: could not set crypto property: %s\n",
564 val
= cpu_to_fdt32(sec_rev_prop_list
[sec_idx
].channel_fifo_len
);
565 err
= fdt_setprop(blob
, crypto_node
, "fsl,channel-fifo-len", &val
, 4);
567 printf("WARNING: could not set crypto property: %s\n",
571 while (sec_idx
>= 0) {
572 p
= compat_strlist
+ val
;
573 val
+= sprintf(p
, "fsl,sec%d.%d",
574 (sec_rev_prop_list
[sec_idx
].sec_rev
& 0xff00) >> 8,
575 sec_rev_prop_list
[sec_idx
].sec_rev
& 0x00ff) + 1;
578 err
= fdt_setprop(blob
, crypto_node
, "compatible", &compat_strlist
, val
);
580 printf("WARNING: could not set crypto property: %s\n",
583 #endif /* defined(CONFIG_MPC83xx) || defined(CONFIG_MPC85xx) */
585 /* Resize the fdt to its actual size + a bit of padding */
586 int fdt_resize(void *blob
)
596 total
= fdt_num_mem_rsv(blob
);
597 for (i
= 0; i
< total
; i
++) {
598 fdt_get_mem_rsv(blob
, i
, &addr
, &size
);
599 if (addr
== (uint64_t)(u32
)blob
) {
600 fdt_del_mem_rsv(blob
, i
);
606 * Calculate the actual size of the fdt
607 * plus the size needed for fdt_add_mem_rsv
609 actualsize
= fdt_off_dt_strings(blob
) +
610 fdt_size_dt_strings(blob
) + sizeof(struct fdt_reserve_entry
);
612 /* Make it so the fdt ends on a page boundary */
613 actualsize
= ALIGN(actualsize
+ ((uint
)blob
& 0xfff), 0x1000);
614 actualsize
= actualsize
- ((uint
)blob
& 0xfff);
616 /* Change the fdt header to reflect the correct size */
617 fdt_set_totalsize(blob
, actualsize
);
619 /* Add the new reservation */
620 ret
= fdt_add_mem_rsv(blob
, (uint
)blob
, actualsize
);
628 #define CONFIG_SYS_PCI_NR_INBOUND_WIN 3
630 #define FDT_PCI_PREFETCH (0x40000000)
631 #define FDT_PCI_MEM32 (0x02000000)
632 #define FDT_PCI_IO (0x01000000)
633 #define FDT_PCI_MEM64 (0x03000000)
635 int fdt_pci_dma_ranges(void *blob
, int phb_off
, struct pci_controller
*hose
) {
637 int addrcell
, sizecell
, len
, r
;
639 /* sized based on pci addr cells, size-cells, & address-cells */
640 u32 dma_ranges
[(3 + 2 + 2) * CONFIG_SYS_PCI_NR_INBOUND_WIN
];
642 addrcell
= fdt_getprop_u32_default(blob
, "/", "#address-cells", 1);
643 sizecell
= fdt_getprop_u32_default(blob
, "/", "#size-cells", 1);
645 dma_range
= &dma_ranges
[0];
646 for (r
= 0; r
< hose
->region_count
; r
++) {
647 u64 bus_start
, phys_start
, size
;
649 /* skip if !PCI_REGION_SYS_MEMORY */
650 if (!(hose
->regions
[r
].flags
& PCI_REGION_SYS_MEMORY
))
653 bus_start
= (u64
)hose
->regions
[r
].bus_start
;
654 phys_start
= (u64
)hose
->regions
[r
].phys_start
;
655 size
= (u64
)hose
->regions
[r
].size
;
658 if (size
> 0x100000000ull
)
659 dma_range
[0] |= FDT_PCI_MEM64
;
661 dma_range
[0] |= FDT_PCI_MEM32
;
662 if (hose
->regions
[r
].flags
& PCI_REGION_PREFETCH
)
663 dma_range
[0] |= FDT_PCI_PREFETCH
;
664 #ifdef CONFIG_SYS_PCI_64BIT
665 dma_range
[1] = bus_start
>> 32;
669 dma_range
[2] = bus_start
& 0xffffffff;
672 dma_range
[3] = phys_start
>> 32;
673 dma_range
[4] = phys_start
& 0xffffffff;
675 dma_range
[3] = phys_start
& 0xffffffff;
679 dma_range
[3 + addrcell
+ 0] = size
>> 32;
680 dma_range
[3 + addrcell
+ 1] = size
& 0xffffffff;
682 dma_range
[3 + addrcell
+ 0] = size
& 0xffffffff;
685 dma_range
+= (3 + addrcell
+ sizecell
);
688 len
= dma_range
- &dma_ranges
[0];
690 fdt_setprop(blob
, phb_off
, "dma-ranges", &dma_ranges
[0], len
*4);