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