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