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 #include <exports.h> 32 33 /* 34 * Global data (for the gd->bd) 35 */ 36 DECLARE_GLOBAL_DATA_PTR; 37 38 /* 39 * fdt points to our working device tree. 40 */ 41 struct fdt_header *fdt; 42 43 /********************************************************************/ 44 45 /** 46 * fdt_find_and_setprop: Find a node and set it's property 47 * 48 * @fdt: ptr to device tree 49 * @node: path of node 50 * @prop: property name 51 * @val: ptr to new value 52 * @len: length of new property value 53 * @create: flag to create the property if it doesn't exist 54 * 55 * Convenience function to directly set a property given the path to the node. 56 */ 57 int fdt_find_and_setprop(void *fdt, const char *node, const char *prop, 58 const void *val, int len, int create) 59 { 60 int nodeoff = fdt_path_offset(fdt, node); 61 62 if (nodeoff < 0) 63 return nodeoff; 64 65 if ((!create) && (fdt_get_property(fdt, nodeoff, prop, 0) == NULL)) 66 return 0; /* create flag not set; so exit quietly */ 67 68 return fdt_setprop(fdt, nodeoff, prop, val, len); 69 } 70 71 #ifdef CONFIG_OF_STDOUT_VIA_ALIAS 72 static int fdt_fixup_stdout(void *fdt, int choosenoff) 73 { 74 int err = 0; 75 #ifdef CONFIG_CONS_INDEX 76 int node; 77 char sername[9] = { 0 }; 78 const char *path; 79 80 sprintf(sername, "serial%d", CONFIG_CONS_INDEX - 1); 81 82 err = node = fdt_path_offset(fdt, "/aliases"); 83 if (node >= 0) { 84 int len; 85 path = fdt_getprop(fdt, node, sername, &len); 86 if (path) { 87 char *p = malloc(len); 88 err = -FDT_ERR_NOSPACE; 89 if (p) { 90 memcpy(p, path, len); 91 err = fdt_setprop(fdt, choosenoff, 92 "linux,stdout-path", p, len); 93 free(p); 94 } 95 } else { 96 err = len; 97 } 98 } 99 #endif 100 if (err < 0) 101 printf("WARNING: could not set linux,stdout-path %s.\n", 102 fdt_strerror(err)); 103 104 return err; 105 } 106 #endif 107 108 int fdt_chosen(void *fdt, ulong initrd_start, ulong initrd_end, int force) 109 { 110 int nodeoffset; 111 int err; 112 u32 tmp; /* used to set 32 bit integer properties */ 113 char *str; /* used to set string properties */ 114 const char *path; 115 116 err = fdt_check_header(fdt); 117 if (err < 0) { 118 printf("fdt_chosen: %s\n", fdt_strerror(err)); 119 return err; 120 } 121 122 if (initrd_start && initrd_end) { 123 uint64_t addr, size; 124 int total = fdt_num_mem_rsv(fdt); 125 int j; 126 127 /* 128 * Look for an existing entry and update it. If we don't find 129 * the entry, we will j be the next available slot. 130 */ 131 for (j = 0; j < total; j++) { 132 err = fdt_get_mem_rsv(fdt, j, &addr, &size); 133 if (addr == initrd_start) { 134 fdt_del_mem_rsv(fdt, j); 135 break; 136 } 137 } 138 139 err = fdt_add_mem_rsv(fdt, initrd_start, initrd_end - initrd_start + 1); 140 if (err < 0) { 141 printf("fdt_chosen: %s\n", fdt_strerror(err)); 142 return err; 143 } 144 } 145 146 /* 147 * Find the "chosen" node. 148 */ 149 nodeoffset = fdt_path_offset (fdt, "/chosen"); 150 151 /* 152 * If there is no "chosen" node in the blob, create it. 153 */ 154 if (nodeoffset < 0) { 155 /* 156 * Create a new node "/chosen" (offset 0 is root level) 157 */ 158 nodeoffset = fdt_add_subnode(fdt, 0, "chosen"); 159 if (nodeoffset < 0) { 160 printf("WARNING: could not create /chosen %s.\n", 161 fdt_strerror(nodeoffset)); 162 return nodeoffset; 163 } 164 } 165 166 /* 167 * Create /chosen properites that don't exist in the fdt. 168 * If the property exists, update it only if the "force" parameter 169 * is true. 170 */ 171 str = getenv("bootargs"); 172 if (str != NULL) { 173 path = fdt_getprop(fdt, nodeoffset, "bootargs", NULL); 174 if ((path == NULL) || force) { 175 err = fdt_setprop(fdt, nodeoffset, 176 "bootargs", str, strlen(str)+1); 177 if (err < 0) 178 printf("WARNING: could not set bootargs %s.\n", 179 fdt_strerror(err)); 180 } 181 } 182 if (initrd_start && initrd_end) { 183 path = fdt_getprop(fdt, nodeoffset, "linux,initrd-start", NULL); 184 if ((path == NULL) || force) { 185 tmp = __cpu_to_be32(initrd_start); 186 err = fdt_setprop(fdt, nodeoffset, 187 "linux,initrd-start", &tmp, sizeof(tmp)); 188 if (err < 0) 189 printf("WARNING: " 190 "could not set linux,initrd-start %s.\n", 191 fdt_strerror(err)); 192 tmp = __cpu_to_be32(initrd_end); 193 err = fdt_setprop(fdt, nodeoffset, 194 "linux,initrd-end", &tmp, sizeof(tmp)); 195 if (err < 0) 196 printf("WARNING: could not set linux,initrd-end %s.\n", 197 fdt_strerror(err)); 198 } 199 } 200 201 #ifdef CONFIG_OF_STDOUT_VIA_ALIAS 202 path = fdt_getprop(fdt, nodeoffset, "linux,stdout-path", NULL); 203 if ((path == NULL) || force) 204 err = fdt_fixup_stdout(fdt, nodeoffset); 205 #endif 206 207 #ifdef OF_STDOUT_PATH 208 path = fdt_getprop(fdt, nodeoffset, "linux,stdout-path", NULL); 209 if ((path == NULL) || force) { 210 err = fdt_setprop(fdt, nodeoffset, 211 "linux,stdout-path", OF_STDOUT_PATH, strlen(OF_STDOUT_PATH)+1); 212 if (err < 0) 213 printf("WARNING: could not set linux,stdout-path %s.\n", 214 fdt_strerror(err)); 215 } 216 #endif 217 218 return err; 219 } 220 221 /********************************************************************/ 222 223 #ifdef CONFIG_OF_HAS_UBOOT_ENV 224 225 /* Function that returns a character from the environment */ 226 extern uchar(*env_get_char) (int); 227 228 229 int fdt_env(void *fdt) 230 { 231 int nodeoffset; 232 int err; 233 int k, nxt; 234 int i; 235 static char tmpenv[256]; 236 237 err = fdt_check_header(fdt); 238 if (err < 0) { 239 printf("fdt_env: %s\n", fdt_strerror(err)); 240 return err; 241 } 242 243 /* 244 * See if we already have a "u-boot-env" node, delete it if so. 245 * Then create a new empty node. 246 */ 247 nodeoffset = fdt_path_offset (fdt, "/u-boot-env"); 248 if (nodeoffset >= 0) { 249 err = fdt_del_node(fdt, nodeoffset); 250 if (err < 0) { 251 printf("fdt_env: %s\n", fdt_strerror(err)); 252 return err; 253 } 254 } 255 /* 256 * Create a new node "/u-boot-env" (offset 0 is root level) 257 */ 258 nodeoffset = fdt_add_subnode(fdt, 0, "u-boot-env"); 259 if (nodeoffset < 0) { 260 printf("WARNING: could not create /u-boot-env %s.\n", 261 fdt_strerror(nodeoffset)); 262 return nodeoffset; 263 } 264 265 for (i = 0; env_get_char(i) != '\0'; i = nxt + 1) { 266 char *s, *lval, *rval; 267 268 /* 269 * Find the end of the name=definition 270 */ 271 for (nxt = i; env_get_char(nxt) != '\0'; ++nxt) 272 ; 273 s = tmpenv; 274 for (k = i; k < nxt && s < &tmpenv[sizeof(tmpenv) - 1]; ++k) 275 *s++ = env_get_char(k); 276 *s++ = '\0'; 277 lval = tmpenv; 278 /* 279 * Find the first '=': it separates the name from the value 280 */ 281 s = strchr(tmpenv, '='); 282 if (s != NULL) { 283 *s++ = '\0'; 284 rval = s; 285 } else 286 continue; 287 err = fdt_setprop(fdt, nodeoffset, lval, rval, strlen(rval)+1); 288 if (err < 0) { 289 printf("WARNING: could not set %s %s.\n", 290 lval, fdt_strerror(err)); 291 return err; 292 } 293 } 294 return 0; 295 } 296 #endif /* ifdef CONFIG_OF_HAS_UBOOT_ENV */ 297 298 /********************************************************************/ 299 300 #ifdef CONFIG_OF_HAS_BD_T 301 302 #define BDM(x) { .name = #x, .offset = offsetof(bd_t, bi_ ##x ) } 303 304 static const struct { 305 const char *name; 306 int offset; 307 } bd_map[] = { 308 BDM(memstart), 309 BDM(memsize), 310 BDM(flashstart), 311 BDM(flashsize), 312 BDM(flashoffset), 313 BDM(sramstart), 314 BDM(sramsize), 315 #if defined(CONFIG_5xx) || defined(CONFIG_8xx) || defined(CONFIG_8260) \ 316 || defined(CONFIG_E500) 317 BDM(immr_base), 318 #endif 319 #if defined(CONFIG_MPC5xxx) 320 BDM(mbar_base), 321 #endif 322 #if defined(CONFIG_MPC83XX) 323 BDM(immrbar), 324 #endif 325 #if defined(CONFIG_MPC8220) 326 BDM(mbar_base), 327 BDM(inpfreq), 328 BDM(pcifreq), 329 BDM(pevfreq), 330 BDM(flbfreq), 331 BDM(vcofreq), 332 #endif 333 BDM(bootflags), 334 BDM(ip_addr), 335 BDM(intfreq), 336 BDM(busfreq), 337 #ifdef CONFIG_CPM2 338 BDM(cpmfreq), 339 BDM(brgfreq), 340 BDM(sccfreq), 341 BDM(vco), 342 #endif 343 #if defined(CONFIG_MPC5xxx) 344 BDM(ipbfreq), 345 BDM(pcifreq), 346 #endif 347 BDM(baudrate), 348 }; 349 350 351 int fdt_bd_t(void *fdt) 352 { 353 bd_t *bd = gd->bd; 354 int nodeoffset; 355 int err; 356 u32 tmp; /* used to set 32 bit integer properties */ 357 int i; 358 359 err = fdt_check_header(fdt); 360 if (err < 0) { 361 printf("fdt_bd_t: %s\n", fdt_strerror(err)); 362 return err; 363 } 364 365 /* 366 * See if we already have a "bd_t" node, delete it if so. 367 * Then create a new empty node. 368 */ 369 nodeoffset = fdt_path_offset (fdt, "/bd_t"); 370 if (nodeoffset >= 0) { 371 err = fdt_del_node(fdt, nodeoffset); 372 if (err < 0) { 373 printf("fdt_bd_t: %s\n", fdt_strerror(err)); 374 return err; 375 } 376 } 377 /* 378 * Create a new node "/bd_t" (offset 0 is root level) 379 */ 380 nodeoffset = fdt_add_subnode(fdt, 0, "bd_t"); 381 if (nodeoffset < 0) { 382 printf("WARNING: could not create /bd_t %s.\n", 383 fdt_strerror(nodeoffset)); 384 printf("fdt_bd_t: %s\n", fdt_strerror(nodeoffset)); 385 return nodeoffset; 386 } 387 /* 388 * Use the string/pointer structure to create the entries... 389 */ 390 for (i = 0; i < sizeof(bd_map)/sizeof(bd_map[0]); i++) { 391 tmp = cpu_to_be32(getenv("bootargs")); 392 err = fdt_setprop(fdt, nodeoffset, 393 bd_map[i].name, &tmp, sizeof(tmp)); 394 if (err < 0) 395 printf("WARNING: could not set %s %s.\n", 396 bd_map[i].name, fdt_strerror(err)); 397 } 398 /* 399 * Add a couple of oddball entries... 400 */ 401 err = fdt_setprop(fdt, nodeoffset, "enetaddr", &bd->bi_enetaddr, 6); 402 if (err < 0) 403 printf("WARNING: could not set enetaddr %s.\n", 404 fdt_strerror(err)); 405 err = fdt_setprop(fdt, nodeoffset, "ethspeed", &bd->bi_ethspeed, 4); 406 if (err < 0) 407 printf("WARNING: could not set ethspeed %s.\n", 408 fdt_strerror(err)); 409 return 0; 410 } 411 #endif /* ifdef CONFIG_OF_HAS_BD_T */ 412 413 void do_fixup_by_path(void *fdt, const char *path, const char *prop, 414 const void *val, int len, int create) 415 { 416 #if defined(DEBUG) 417 int i; 418 debug("Updating property '%s/%s' = ", node, prop); 419 for (i = 0; i < len; i++) 420 debug(" %.2x", *(u8*)(val+i)); 421 debug("\n"); 422 #endif 423 int rc = fdt_find_and_setprop(fdt, path, prop, val, len, create); 424 if (rc) 425 printf("Unable to update property %s:%s, err=%s\n", 426 path, prop, fdt_strerror(rc)); 427 } 428 429 void do_fixup_by_path_u32(void *fdt, const char *path, const char *prop, 430 u32 val, int create) 431 { 432 val = cpu_to_fdt32(val); 433 do_fixup_by_path(fdt, path, prop, &val, sizeof(val), create); 434 } 435 436 void do_fixup_by_prop(void *fdt, 437 const char *pname, const void *pval, int plen, 438 const char *prop, const void *val, int len, 439 int create) 440 { 441 int off; 442 #if defined(DEBUG) 443 int i; 444 debug("Updating property '%s/%s' = ", node, prop); 445 for (i = 0; i < len; i++) 446 debug(" %.2x", *(u8*)(val+i)); 447 debug("\n"); 448 #endif 449 off = fdt_node_offset_by_prop_value(fdt, -1, pname, pval, plen); 450 while (off != -FDT_ERR_NOTFOUND) { 451 if (create || (fdt_get_property(fdt, off, prop, 0) != NULL)) 452 fdt_setprop(fdt, off, prop, val, len); 453 off = fdt_node_offset_by_prop_value(fdt, off, pname, pval, plen); 454 } 455 } 456 457 void do_fixup_by_prop_u32(void *fdt, 458 const char *pname, const void *pval, int plen, 459 const char *prop, u32 val, int create) 460 { 461 val = cpu_to_fdt32(val); 462 do_fixup_by_prop(fdt, pname, pval, plen, prop, &val, 4, create); 463 } 464 465 void do_fixup_by_compat(void *fdt, const char *compat, 466 const char *prop, const void *val, int len, int create) 467 { 468 int off = -1; 469 #if defined(DEBUG) 470 int i; 471 debug("Updating property '%s/%s' = ", node, prop); 472 for (i = 0; i < len; i++) 473 debug(" %.2x", *(u8*)(val+i)); 474 debug("\n"); 475 #endif 476 off = fdt_node_offset_by_compatible(fdt, -1, compat); 477 while (off != -FDT_ERR_NOTFOUND) { 478 if (create || (fdt_get_property(fdt, off, prop, 0) != NULL)) 479 fdt_setprop(fdt, off, prop, val, len); 480 off = fdt_node_offset_by_compatible(fdt, off, compat); 481 } 482 } 483 484 void do_fixup_by_compat_u32(void *fdt, const char *compat, 485 const char *prop, u32 val, int create) 486 { 487 val = cpu_to_fdt32(val); 488 do_fixup_by_compat(fdt, compat, prop, &val, 4, create); 489 } 490 491 int fdt_fixup_memory(void *blob, u64 start, u64 size) 492 { 493 int err, nodeoffset, len = 0; 494 u8 tmp[16]; 495 const u32 *addrcell, *sizecell; 496 497 err = fdt_check_header(blob); 498 if (err < 0) { 499 printf("%s: %s\n", __FUNCTION__, fdt_strerror(err)); 500 return err; 501 } 502 503 /* update, or add and update /memory node */ 504 nodeoffset = fdt_path_offset(blob, "/memory"); 505 if (nodeoffset < 0) { 506 nodeoffset = fdt_add_subnode(blob, 0, "memory"); 507 if (nodeoffset < 0) 508 printf("WARNING: could not create /memory: %s.\n", 509 fdt_strerror(nodeoffset)); 510 return nodeoffset; 511 } 512 err = fdt_setprop(blob, nodeoffset, "device_type", "memory", 513 sizeof("memory")); 514 if (err < 0) { 515 printf("WARNING: could not set %s %s.\n", "device_type", 516 fdt_strerror(err)); 517 return err; 518 } 519 520 addrcell = fdt_getprop(blob, 0, "#address-cells", NULL); 521 /* use shifts and mask to ensure endianness */ 522 if ((addrcell) && (*addrcell == 2)) { 523 tmp[0] = (start >> 56) & 0xff; 524 tmp[1] = (start >> 48) & 0xff; 525 tmp[2] = (start >> 40) & 0xff; 526 tmp[3] = (start >> 32) & 0xff; 527 tmp[4] = (start >> 24) & 0xff; 528 tmp[5] = (start >> 16) & 0xff; 529 tmp[6] = (start >> 8) & 0xff; 530 tmp[7] = (start ) & 0xff; 531 len = 8; 532 } else { 533 tmp[0] = (start >> 24) & 0xff; 534 tmp[1] = (start >> 16) & 0xff; 535 tmp[2] = (start >> 8) & 0xff; 536 tmp[3] = (start ) & 0xff; 537 len = 4; 538 } 539 540 sizecell = fdt_getprop(blob, 0, "#size-cells", NULL); 541 /* use shifts and mask to ensure endianness */ 542 if ((sizecell) && (*sizecell == 2)) { 543 tmp[0+len] = (size >> 56) & 0xff; 544 tmp[1+len] = (size >> 48) & 0xff; 545 tmp[2+len] = (size >> 40) & 0xff; 546 tmp[3+len] = (size >> 32) & 0xff; 547 tmp[4+len] = (size >> 24) & 0xff; 548 tmp[5+len] = (size >> 16) & 0xff; 549 tmp[6+len] = (size >> 8) & 0xff; 550 tmp[7+len] = (size ) & 0xff; 551 len += 8; 552 } else { 553 tmp[0+len] = (size >> 24) & 0xff; 554 tmp[1+len] = (size >> 16) & 0xff; 555 tmp[2+len] = (size >> 8) & 0xff; 556 tmp[3+len] = (size ) & 0xff; 557 len += 4; 558 } 559 560 err = fdt_setprop(blob, nodeoffset, "reg", tmp, len); 561 if (err < 0) { 562 printf("WARNING: could not set %s %s.\n", 563 "reg", fdt_strerror(err)); 564 return err; 565 } 566 return 0; 567 } 568 569 #if defined(CONFIG_HAS_ETH0) || defined(CONFIG_HAS_ETH1) ||\ 570 defined(CONFIG_HAS_ETH2) || defined(CONFIG_HAS_ETH3) 571 572 void fdt_fixup_ethernet(void *fdt, bd_t *bd) 573 { 574 int node; 575 const char *path; 576 577 node = fdt_path_offset(fdt, "/aliases"); 578 if (node >= 0) { 579 #if defined(CONFIG_HAS_ETH0) 580 path = fdt_getprop(fdt, node, "ethernet0", NULL); 581 if (path) { 582 do_fixup_by_path(fdt, path, "mac-address", 583 bd->bi_enetaddr, 6, 0); 584 do_fixup_by_path(fdt, path, "local-mac-address", 585 bd->bi_enetaddr, 6, 1); 586 } 587 #endif 588 #if defined(CONFIG_HAS_ETH1) 589 path = fdt_getprop(fdt, node, "ethernet1", NULL); 590 if (path) { 591 do_fixup_by_path(fdt, path, "mac-address", 592 bd->bi_enet1addr, 6, 0); 593 do_fixup_by_path(fdt, path, "local-mac-address", 594 bd->bi_enet1addr, 6, 1); 595 } 596 #endif 597 #if defined(CONFIG_HAS_ETH2) 598 path = fdt_getprop(fdt, node, "ethernet2", NULL); 599 if (path) { 600 do_fixup_by_path(fdt, path, "mac-address", 601 bd->bi_enet2addr, 6, 0); 602 do_fixup_by_path(fdt, path, "local-mac-address", 603 bd->bi_enet2addr, 6, 1); 604 } 605 #endif 606 #if defined(CONFIG_HAS_ETH3) 607 path = fdt_getprop(fdt, node, "ethernet3", NULL); 608 if (path) { 609 do_fixup_by_path(fdt, path, "mac-address", 610 bd->bi_enet3addr, 6, 0); 611 do_fixup_by_path(fdt, path, "local-mac-address", 612 bd->bi_enet3addr, 6, 1); 613 } 614 #endif 615 } 616 } 617 #endif 618