1 /* 2 * (C) Copyright 2007 3 * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com 4 * 5 * See file CREDITS for list of people who contributed to this 6 * project. 7 * 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. 12 * 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. 17 * 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, 21 * MA 02111-1307 USA 22 */ 23 24 #include <common.h> 25 #include <linux/ctype.h> 26 #include <linux/types.h> 27 #include <asm/global_data.h> 28 #include <fdt.h> 29 #include <libfdt.h> 30 #include <fdt_support.h> 31 32 /* 33 * Global data (for the gd->bd) 34 */ 35 DECLARE_GLOBAL_DATA_PTR; 36 37 /* 38 * fdt points to our working device tree. 39 */ 40 struct fdt_header *fdt; 41 42 /********************************************************************/ 43 44 /** 45 * fdt_find_and_setprop: Find a node and set it's property 46 * 47 * @fdt: ptr to device tree 48 * @node: path of node 49 * @prop: property name 50 * @val: ptr to new value 51 * @len: length of new property value 52 * @create: flag to create the property if it doesn't exist 53 * 54 * Convenience function to directly set a property given the path to the node. 55 */ 56 int fdt_find_and_setprop(void *fdt, const char *node, const char *prop, 57 const void *val, int len, int create) 58 { 59 int nodeoff = fdt_path_offset(fdt, node); 60 61 if (nodeoff < 0) 62 return nodeoff; 63 64 if ((!create) && (fdt_get_property(fdt, nodeoff, prop, 0) == NULL)) 65 return 0; /* create flag not set; so exit quietly */ 66 67 return fdt_setprop(fdt, nodeoff, prop, val, len); 68 } 69 70 int fdt_chosen(void *fdt, ulong initrd_start, ulong initrd_end, int force) 71 { 72 int nodeoffset; 73 int err; 74 u32 tmp; /* used to set 32 bit integer properties */ 75 char *str; /* used to set string properties */ 76 77 err = fdt_check_header(fdt); 78 if (err < 0) { 79 printf("fdt_chosen: %s\n", fdt_strerror(err)); 80 return err; 81 } 82 83 if (initrd_start && initrd_end) { 84 uint64_t addr, size; 85 int total = fdt_num_mem_rsv(fdt); 86 int j; 87 88 /* 89 * Look for an existing entry and update it. If we don't find 90 * the entry, we will j be the next available slot. 91 */ 92 for (j = 0; j < total; j++) { 93 err = fdt_get_mem_rsv(fdt, j, &addr, &size); 94 if (addr == initrd_start) { 95 fdt_del_mem_rsv(fdt, j); 96 break; 97 } 98 } 99 100 err = fdt_add_mem_rsv(fdt, initrd_start, initrd_end - initrd_start + 1); 101 if (err < 0) { 102 printf("fdt_chosen: %s\n", fdt_strerror(err)); 103 return err; 104 } 105 } 106 107 /* 108 * Find the "chosen" node. 109 */ 110 nodeoffset = fdt_path_offset (fdt, "/chosen"); 111 112 /* 113 * If we have a "chosen" node already the "force the writing" 114 * is not set, our job is done. 115 */ 116 if ((nodeoffset >= 0) && !force) 117 return 0; 118 119 /* 120 * No "chosen" node in the blob: create it. 121 */ 122 if (nodeoffset < 0) { 123 /* 124 * Create a new node "/chosen" (offset 0 is root level) 125 */ 126 nodeoffset = fdt_add_subnode(fdt, 0, "chosen"); 127 if (nodeoffset < 0) { 128 printf("WARNING: could not create /chosen %s.\n", 129 fdt_strerror(nodeoffset)); 130 return nodeoffset; 131 } 132 } 133 134 /* 135 * Update pre-existing properties, create them if non-existant. 136 */ 137 str = getenv("bootargs"); 138 if (str != NULL) { 139 err = fdt_setprop(fdt, nodeoffset, 140 "bootargs", str, strlen(str)+1); 141 if (err < 0) 142 printf("WARNING: could not set bootargs %s.\n", 143 fdt_strerror(err)); 144 } 145 if (initrd_start && initrd_end) { 146 tmp = __cpu_to_be32(initrd_start); 147 err = fdt_setprop(fdt, nodeoffset, 148 "linux,initrd-start", &tmp, sizeof(tmp)); 149 if (err < 0) 150 printf("WARNING: " 151 "could not set linux,initrd-start %s.\n", 152 fdt_strerror(err)); 153 tmp = __cpu_to_be32(initrd_end); 154 err = fdt_setprop(fdt, nodeoffset, 155 "linux,initrd-end", &tmp, sizeof(tmp)); 156 if (err < 0) 157 printf("WARNING: could not set linux,initrd-end %s.\n", 158 fdt_strerror(err)); 159 } 160 #ifdef OF_STDOUT_PATH 161 err = fdt_setprop(fdt, nodeoffset, 162 "linux,stdout-path", OF_STDOUT_PATH, strlen(OF_STDOUT_PATH)+1); 163 if (err < 0) 164 printf("WARNING: could not set linux,stdout-path %s.\n", 165 fdt_strerror(err)); 166 #endif 167 168 return err; 169 } 170 171 /********************************************************************/ 172 173 #ifdef CONFIG_OF_HAS_UBOOT_ENV 174 175 /* Function that returns a character from the environment */ 176 extern uchar(*env_get_char) (int); 177 178 179 int fdt_env(void *fdt) 180 { 181 int nodeoffset; 182 int err; 183 int k, nxt; 184 int i; 185 static char tmpenv[256]; 186 187 err = fdt_check_header(fdt); 188 if (err < 0) { 189 printf("fdt_env: %s\n", fdt_strerror(err)); 190 return err; 191 } 192 193 /* 194 * See if we already have a "u-boot-env" node, delete it if so. 195 * Then create a new empty node. 196 */ 197 nodeoffset = fdt_path_offset (fdt, "/u-boot-env"); 198 if (nodeoffset >= 0) { 199 err = fdt_del_node(fdt, nodeoffset); 200 if (err < 0) { 201 printf("fdt_env: %s\n", fdt_strerror(err)); 202 return err; 203 } 204 } 205 /* 206 * Create a new node "/u-boot-env" (offset 0 is root level) 207 */ 208 nodeoffset = fdt_add_subnode(fdt, 0, "u-boot-env"); 209 if (nodeoffset < 0) { 210 printf("WARNING: could not create /u-boot-env %s.\n", 211 fdt_strerror(nodeoffset)); 212 return nodeoffset; 213 } 214 215 for (i = 0; env_get_char(i) != '\0'; i = nxt + 1) { 216 char *s, *lval, *rval; 217 218 /* 219 * Find the end of the name=definition 220 */ 221 for (nxt = i; env_get_char(nxt) != '\0'; ++nxt) 222 ; 223 s = tmpenv; 224 for (k = i; k < nxt && s < &tmpenv[sizeof(tmpenv) - 1]; ++k) 225 *s++ = env_get_char(k); 226 *s++ = '\0'; 227 lval = tmpenv; 228 /* 229 * Find the first '=': it separates the name from the value 230 */ 231 s = strchr(tmpenv, '='); 232 if (s != NULL) { 233 *s++ = '\0'; 234 rval = s; 235 } else 236 continue; 237 err = fdt_setprop(fdt, nodeoffset, lval, rval, strlen(rval)+1); 238 if (err < 0) { 239 printf("WARNING: could not set %s %s.\n", 240 lval, fdt_strerror(err)); 241 return err; 242 } 243 } 244 return 0; 245 } 246 #endif /* ifdef CONFIG_OF_HAS_UBOOT_ENV */ 247 248 /********************************************************************/ 249 250 #ifdef CONFIG_OF_HAS_BD_T 251 252 #define BDM(x) { .name = #x, .offset = offsetof(bd_t, bi_ ##x ) } 253 254 static const struct { 255 const char *name; 256 int offset; 257 } bd_map[] = { 258 BDM(memstart), 259 BDM(memsize), 260 BDM(flashstart), 261 BDM(flashsize), 262 BDM(flashoffset), 263 BDM(sramstart), 264 BDM(sramsize), 265 #if defined(CONFIG_5xx) || defined(CONFIG_8xx) || defined(CONFIG_8260) \ 266 || defined(CONFIG_E500) 267 BDM(immr_base), 268 #endif 269 #if defined(CONFIG_MPC5xxx) 270 BDM(mbar_base), 271 #endif 272 #if defined(CONFIG_MPC83XX) 273 BDM(immrbar), 274 #endif 275 #if defined(CONFIG_MPC8220) 276 BDM(mbar_base), 277 BDM(inpfreq), 278 BDM(pcifreq), 279 BDM(pevfreq), 280 BDM(flbfreq), 281 BDM(vcofreq), 282 #endif 283 BDM(bootflags), 284 BDM(ip_addr), 285 BDM(intfreq), 286 BDM(busfreq), 287 #ifdef CONFIG_CPM2 288 BDM(cpmfreq), 289 BDM(brgfreq), 290 BDM(sccfreq), 291 BDM(vco), 292 #endif 293 #if defined(CONFIG_MPC5xxx) 294 BDM(ipbfreq), 295 BDM(pcifreq), 296 #endif 297 BDM(baudrate), 298 }; 299 300 301 int fdt_bd_t(void *fdt) 302 { 303 bd_t *bd = gd->bd; 304 int nodeoffset; 305 int err; 306 u32 tmp; /* used to set 32 bit integer properties */ 307 int i; 308 309 err = fdt_check_header(fdt); 310 if (err < 0) { 311 printf("fdt_bd_t: %s\n", fdt_strerror(err)); 312 return err; 313 } 314 315 /* 316 * See if we already have a "bd_t" node, delete it if so. 317 * Then create a new empty node. 318 */ 319 nodeoffset = fdt_path_offset (fdt, "/bd_t"); 320 if (nodeoffset >= 0) { 321 err = fdt_del_node(fdt, nodeoffset); 322 if (err < 0) { 323 printf("fdt_bd_t: %s\n", fdt_strerror(err)); 324 return err; 325 } 326 } 327 /* 328 * Create a new node "/bd_t" (offset 0 is root level) 329 */ 330 nodeoffset = fdt_add_subnode(fdt, 0, "bd_t"); 331 if (nodeoffset < 0) { 332 printf("WARNING: could not create /bd_t %s.\n", 333 fdt_strerror(nodeoffset)); 334 printf("fdt_bd_t: %s\n", fdt_strerror(nodeoffset)); 335 return nodeoffset; 336 } 337 /* 338 * Use the string/pointer structure to create the entries... 339 */ 340 for (i = 0; i < sizeof(bd_map)/sizeof(bd_map[0]); i++) { 341 tmp = cpu_to_be32(getenv("bootargs")); 342 err = fdt_setprop(fdt, nodeoffset, 343 bd_map[i].name, &tmp, sizeof(tmp)); 344 if (err < 0) 345 printf("WARNING: could not set %s %s.\n", 346 bd_map[i].name, fdt_strerror(err)); 347 } 348 /* 349 * Add a couple of oddball entries... 350 */ 351 err = fdt_setprop(fdt, nodeoffset, "enetaddr", &bd->bi_enetaddr, 6); 352 if (err < 0) 353 printf("WARNING: could not set enetaddr %s.\n", 354 fdt_strerror(err)); 355 err = fdt_setprop(fdt, nodeoffset, "ethspeed", &bd->bi_ethspeed, 4); 356 if (err < 0) 357 printf("WARNING: could not set ethspeed %s.\n", 358 fdt_strerror(err)); 359 return 0; 360 } 361 #endif /* ifdef CONFIG_OF_HAS_BD_T */ 362 363 void do_fixup_by_path(void *fdt, const char *path, const char *prop, 364 const void *val, int len, int create) 365 { 366 #if defined(DEBUG) 367 int i; 368 debug("Updating property '%s/%s' = ", node, prop); 369 for (i = 0; i < len; i++) 370 debug(" %.2x", *(u8*)(val+i)); 371 debug("\n"); 372 #endif 373 int rc = fdt_find_and_setprop(fdt, path, prop, val, len, create); 374 if (rc) 375 printf("Unable to update property %s:%s, err=%s\n", 376 path, prop, fdt_strerror(rc)); 377 } 378 379 void do_fixup_by_path_u32(void *fdt, const char *path, const char *prop, 380 u32 val, int create) 381 { 382 val = cpu_to_fdt32(val); 383 do_fixup_by_path(fdt, path, prop, &val, sizeof(val), create); 384 } 385 386 void do_fixup_by_prop(void *fdt, 387 const char *pname, const void *pval, int plen, 388 const char *prop, const void *val, int len, 389 int create) 390 { 391 int off; 392 #if defined(DEBUG) 393 int i; 394 debug("Updating property '%s/%s' = ", node, prop); 395 for (i = 0; i < len; i++) 396 debug(" %.2x", *(u8*)(val+i)); 397 debug("\n"); 398 #endif 399 off = fdt_node_offset_by_prop_value(fdt, -1, pname, pval, plen); 400 while (off != -FDT_ERR_NOTFOUND) { 401 if (create || (fdt_get_property(fdt, off, prop, 0) != NULL)) 402 fdt_setprop(fdt, off, prop, val, len); 403 off = fdt_node_offset_by_prop_value(fdt, off, pname, pval, plen); 404 } 405 } 406 407 void do_fixup_by_prop_u32(void *fdt, 408 const char *pname, const void *pval, int plen, 409 const char *prop, u32 val, int create) 410 { 411 val = cpu_to_fdt32(val); 412 do_fixup_by_prop(fdt, pname, pval, plen, prop, &val, 4, create); 413 } 414 415 void do_fixup_by_compat(void *fdt, const char *compat, 416 const char *prop, const void *val, int len, int create) 417 { 418 int off = -1; 419 #if defined(DEBUG) 420 int i; 421 debug("Updating property '%s/%s' = ", node, prop); 422 for (i = 0; i < len; i++) 423 debug(" %.2x", *(u8*)(val+i)); 424 debug("\n"); 425 #endif 426 off = fdt_node_offset_by_compatible(fdt, -1, compat); 427 while (off != -FDT_ERR_NOTFOUND) { 428 if (create || (fdt_get_property(fdt, off, prop, 0) != NULL)) 429 fdt_setprop(fdt, off, prop, val, len); 430 off = fdt_node_offset_by_compatible(fdt, off, compat); 431 } 432 } 433 434 void do_fixup_by_compat_u32(void *fdt, const char *compat, 435 const char *prop, u32 val, int create) 436 { 437 val = cpu_to_fdt32(val); 438 do_fixup_by_compat(fdt, compat, prop, &val, 4, create); 439 } 440 441 void fdt_fixup_ethernet(void *fdt, bd_t *bd) 442 { 443 int node; 444 const char *path; 445 446 node = fdt_path_offset(fdt, "/aliases"); 447 if (node >= 0) { 448 #if defined(CONFIG_HAS_ETH0) 449 path = fdt_getprop(fdt, node, "ethernet0", NULL); 450 if (path) { 451 do_fixup_by_path(fdt, path, "mac-address", 452 bd->bi_enetaddr, 6, 0); 453 do_fixup_by_path(fdt, path, "local-mac-address", 454 bd->bi_enetaddr, 6, 1); 455 } 456 #endif 457 #if defined(CONFIG_HAS_ETH1) 458 path = fdt_getprop(fdt, node, "ethernet1", NULL); 459 if (path) { 460 do_fixup_by_path(fdt, path, "mac-address", 461 bd->bi_enet1addr, 6, 0); 462 do_fixup_by_path(fdt, path, "local-mac-address", 463 bd->bi_enet1addr, 6, 1); 464 } 465 #endif 466 #if defined(CONFIG_HAS_ETH2) 467 path = fdt_getprop(fdt, node, "ethernet2", NULL); 468 if (path) { 469 do_fixup_by_path(fdt, path, "mac-address", 470 bd->bi_enet2addr, 6, 0); 471 do_fixup_by_path(fdt, path, "local-mac-address", 472 bd->bi_enet2addr, 6, 1); 473 } 474 #endif 475 #if defined(CONFIG_HAS_ETH3) 476 path = fdt_getprop(fdt, node, "ethernet3", NULL); 477 if (path) { 478 do_fixup_by_path(fdt, path, "mac-address", 479 bd->bi_enet3addr, 6, 0); 480 do_fixup_by_path(fdt, path, "local-mac-address", 481 bd->bi_enet3addr, 6, 1); 482 } 483 #endif 484 } 485 } 486