| /* |
| * (C) Copyright 2007 |
| * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com |
| * |
| * See file CREDITS for list of people who contributed to this |
| * project. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of |
| * the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
| * MA 02111-1307 USA |
| */ |
| |
| #include <common.h> |
| #include <linux/ctype.h> |
| #include <linux/types.h> |
| |
| #ifdef CONFIG_OF_LIBFDT |
| |
| #include <asm/global_data.h> |
| #include <fdt.h> |
| #include <libfdt.h> |
| #include <fdt_support.h> |
| |
| /* |
| * Global data (for the gd->bd) |
| */ |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| |
| /********************************************************************/ |
| |
| int fdt_chosen(void *fdt, ulong initrd_start, ulong initrd_end, int force) |
| { |
| bd_t *bd = gd->bd; |
| int nodeoffset; |
| int err; |
| u32 tmp; /* used to set 32 bit integer properties */ |
| char *str; /* used to set string properties */ |
| ulong clock; |
| |
| err = fdt_check_header(fdt); |
| if (err < 0) { |
| printf("libfdt: %s\n", fdt_strerror(err)); |
| return err; |
| } |
| |
| if (initrd_start && initrd_end) { |
| struct fdt_reserve_entry re; |
| int used; |
| int total; |
| int j; |
| |
| err = fdt_num_reservemap(fdt, &used, &total); |
| if (err < 0) { |
| printf("libfdt: %s\n", fdt_strerror(err)); |
| return err; |
| } |
| if (used >= total) { |
| printf("fdt_chosen: no room in the reserved map (%d of %d)\n", |
| used, total); |
| return -1; |
| } |
| /* |
| * Look for an existing entry and update it. If we don't find |
| * the entry, we will j be the next available slot. |
| */ |
| for (j = 0; j < used; j++) { |
| err = fdt_get_reservemap(fdt, j, &re); |
| if (re.address == initrd_start) { |
| break; |
| } |
| } |
| err = fdt_replace_reservemap_entry(fdt, j, |
| initrd_start, initrd_end - initrd_start + 1); |
| if (err < 0) { |
| printf("libfdt: %s\n", fdt_strerror(err)); |
| return err; |
| } |
| } |
| |
| /* |
| * Find the "chosen" node. |
| */ |
| nodeoffset = fdt_path_offset (fdt, "/chosen"); |
| |
| /* |
| * If we have a "chosen" node already the "force the writing" |
| * is not set, our job is done. |
| */ |
| if ((nodeoffset >= 0) && !force) |
| return 0; |
| |
| /* |
| * No "chosen" node in the blob: create it. |
| */ |
| if (nodeoffset < 0) { |
| /* |
| * Create a new node "/chosen" (offset 0 is root level) |
| */ |
| nodeoffset = fdt_add_subnode(fdt, 0, "chosen"); |
| if (nodeoffset < 0) { |
| printf("libfdt: %s\n", fdt_strerror(nodeoffset)); |
| return nodeoffset; |
| } |
| } |
| |
| /* |
| * Update pre-existing properties, create them if non-existant. |
| */ |
| str = getenv("bootargs"); |
| if (str != NULL) { |
| err = fdt_setprop(fdt, nodeoffset, "bootargs", str, strlen(str)+1); |
| if (err < 0) |
| printf("libfdt: %s\n", fdt_strerror(err)); |
| } |
| if (initrd_start && initrd_end) { |
| tmp = __cpu_to_be32(initrd_start); |
| err = fdt_setprop(fdt, nodeoffset, "linux,initrd-start", &tmp, sizeof(tmp)); |
| if (err < 0) |
| printf("libfdt: %s\n", fdt_strerror(err)); |
| tmp = __cpu_to_be32(initrd_end); |
| err = fdt_setprop(fdt, nodeoffset, "linux,initrd-end", &tmp, sizeof(tmp)); |
| if (err < 0) |
| printf("libfdt: %s\n", fdt_strerror(err)); |
| } |
| #ifdef OF_STDOUT_PATH |
| err = fdt_setprop(fdt, nodeoffset, "linux,stdout-path", OF_STDOUT_PATH, strlen(OF_STDOUT_PATH)+1); |
| if (err < 0) |
| printf("libfdt: %s\n", fdt_strerror(err)); |
| #endif |
| |
| nodeoffset = fdt_path_offset (fdt, "/cpus"); |
| if (nodeoffset >= 0) { |
| clock = cpu_to_be32(bd->bi_intfreq); |
| err = fdt_setprop(fdt, nodeoffset, "clock-frequency", &clock, 4); |
| if (err < 0) |
| printf("libfdt: %s\n", fdt_strerror(err)); |
| } |
| #ifdef OF_TBCLK |
| nodeoffset = fdt_path_offset (fdt, "/cpus/" OF_CPU "/timebase-frequency"); |
| if (nodeoffset >= 0) { |
| clock = cpu_to_be32(OF_TBCLK); |
| err = fdt_setprop(fdt, nodeoffset, "clock-frequency", &clock, 4); |
| if (err < 0) |
| printf("libfdt: %s\n", fdt_strerror(err)); |
| } |
| #endif |
| return err; |
| } |
| |
| /********************************************************************/ |
| |
| #ifdef CONFIG_OF_HAS_UBOOT_ENV |
| |
| /* Function that returns a character from the environment */ |
| extern uchar(*env_get_char) (int); |
| |
| |
| int fdt_env(void *fdt) |
| { |
| int nodeoffset; |
| int err; |
| int k, nxt; |
| int i; |
| static char tmpenv[256]; |
| |
| err = fdt_check_header(fdt); |
| if (err < 0) { |
| printf("libfdt: %s\n", fdt_strerror(err)); |
| return err; |
| } |
| |
| /* |
| * See if we already have a "u-boot-env" node, delete it if so. |
| * Then create a new empty node. |
| */ |
| nodeoffset = fdt_path_offset (fdt, "/u-boot-env"); |
| if (nodeoffset >= 0) { |
| err = fdt_del_node(fdt, nodeoffset); |
| if (err < 0) { |
| printf("libfdt: %s\n", fdt_strerror(err)); |
| return err; |
| } |
| } |
| /* |
| * Create a new node "/u-boot-env" (offset 0 is root level) |
| */ |
| nodeoffset = fdt_add_subnode(fdt, 0, "u-boot-env"); |
| if (nodeoffset < 0) { |
| printf("libfdt: %s\n", fdt_strerror(nodeoffset)); |
| return nodeoffset; |
| } |
| |
| for (i = 0; env_get_char(i) != '\0'; i = nxt + 1) { |
| char *s, *lval, *rval; |
| |
| /* |
| * Find the end of the name=definition |
| */ |
| for (nxt = i; env_get_char(nxt) != '\0'; ++nxt) |
| ; |
| s = tmpenv; |
| for (k = i; k < nxt && s < &tmpenv[sizeof(tmpenv) - 1]; ++k) |
| *s++ = env_get_char(k); |
| *s++ = '\0'; |
| lval = tmpenv; |
| /* |
| * Find the first '=': it separates the name from the value |
| */ |
| s = strchr(tmpenv, '='); |
| if (s != NULL) { |
| *s++ = '\0'; |
| rval = s; |
| } else |
| continue; |
| err = fdt_setprop(fdt, nodeoffset, lval, rval, strlen(rval)+1); |
| if (err < 0) { |
| printf("libfdt: %s\n", fdt_strerror(err)); |
| return err; |
| } |
| } |
| return 0; |
| } |
| #endif /* ifdef CONFIG_OF_HAS_UBOOT_ENV */ |
| |
| /********************************************************************/ |
| |
| #ifdef CONFIG_OF_HAS_BD_T |
| |
| #define BDM(x) { .name = #x, .offset = offsetof(bd_t, bi_ ##x ) } |
| |
| static const struct { |
| const char *name; |
| int offset; |
| } bd_map[] = { |
| BDM(memstart), |
| BDM(memsize), |
| BDM(flashstart), |
| BDM(flashsize), |
| BDM(flashoffset), |
| BDM(sramstart), |
| BDM(sramsize), |
| #if defined(CONFIG_5xx) || defined(CONFIG_8xx) || defined(CONFIG_8260) \ |
| || defined(CONFIG_E500) |
| BDM(immr_base), |
| #endif |
| #if defined(CONFIG_MPC5xxx) |
| BDM(mbar_base), |
| #endif |
| #if defined(CONFIG_MPC83XX) |
| BDM(immrbar), |
| #endif |
| #if defined(CONFIG_MPC8220) |
| BDM(mbar_base), |
| BDM(inpfreq), |
| BDM(pcifreq), |
| BDM(pevfreq), |
| BDM(flbfreq), |
| BDM(vcofreq), |
| #endif |
| BDM(bootflags), |
| BDM(ip_addr), |
| BDM(intfreq), |
| BDM(busfreq), |
| #ifdef CONFIG_CPM2 |
| BDM(cpmfreq), |
| BDM(brgfreq), |
| BDM(sccfreq), |
| BDM(vco), |
| #endif |
| #if defined(CONFIG_MPC5xxx) |
| BDM(ipbfreq), |
| BDM(pcifreq), |
| #endif |
| BDM(baudrate), |
| }; |
| |
| |
| int fdt_bd_t(void *fdt) |
| { |
| bd_t *bd = gd->bd; |
| int nodeoffset; |
| int err; |
| u32 tmp; /* used to set 32 bit integer properties */ |
| int i; |
| |
| err = fdt_check_header(fdt); |
| if (err < 0) { |
| printf("libfdt: %s\n", fdt_strerror(err)); |
| return err; |
| } |
| |
| /* |
| * See if we already have a "bd_t" node, delete it if so. |
| * Then create a new empty node. |
| */ |
| nodeoffset = fdt_path_offset (fdt, "/bd_t"); |
| if (nodeoffset >= 0) { |
| err = fdt_del_node(fdt, nodeoffset); |
| if (err < 0) { |
| printf("libfdt: %s\n", fdt_strerror(err)); |
| return err; |
| } |
| } |
| /* |
| * Create a new node "/bd_t" (offset 0 is root level) |
| */ |
| nodeoffset = fdt_add_subnode(fdt, 0, "bd_t"); |
| if (nodeoffset < 0) { |
| printf("libfdt: %s\n", fdt_strerror(nodeoffset)); |
| return nodeoffset; |
| } |
| /* |
| * Use the string/pointer structure to create the entries... |
| */ |
| for (i = 0; i < sizeof(bd_map)/sizeof(bd_map[0]); i++) { |
| tmp = cpu_to_be32(getenv("bootargs")); |
| err = fdt_setprop(fdt, nodeoffset, bd_map[i].name, &tmp, sizeof(tmp)); |
| if (err < 0) |
| printf("libfdt: %s\n", fdt_strerror(err)); |
| } |
| /* |
| * Add a couple of oddball entries... |
| */ |
| err = fdt_setprop(fdt, nodeoffset, "enetaddr", &bd->bi_enetaddr, 6); |
| if (err < 0) |
| printf("libfdt: %s\n", fdt_strerror(err)); |
| err = fdt_setprop(fdt, nodeoffset, "ethspeed", &bd->bi_ethspeed, 4); |
| if (err < 0) |
| printf("libfdt: %s\n", fdt_strerror(err)); |
| |
| return 0; |
| } |
| #endif /* ifdef CONFIG_OF_HAS_BD_T */ |
| |
| #endif /* CONFIG_OF_LIBFDT */ |