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 int fdt_fixup_memory(void *blob, u64 start, u64 size) 442 { 443 int err, nodeoffset, len = 0; 444 u8 tmp[16]; 445 const u32 *addrcell, *sizecell; 446 447 err = fdt_check_header(blob); 448 if (err < 0) { 449 printf("%s: %s\n", __FUNCTION__, fdt_strerror(err)); 450 return err; 451 } 452 453 /* update, or add and update /memory node */ 454 nodeoffset = fdt_path_offset(blob, "/memory"); 455 if (nodeoffset < 0) { 456 nodeoffset = fdt_add_subnode(blob, 0, "memory"); 457 if (nodeoffset < 0) 458 printf("WARNING: could not create /memory: %s.\n", 459 fdt_strerror(nodeoffset)); 460 return nodeoffset; 461 } 462 err = fdt_setprop(blob, nodeoffset, "device_type", "memory", 463 sizeof("memory")); 464 if (err < 0) { 465 printf("WARNING: could not set %s %s.\n", "device_type", 466 fdt_strerror(err)); 467 return err; 468 } 469 470 addrcell = fdt_getprop(blob, 0, "#address-cells", NULL); 471 /* use shifts and mask to ensure endianness */ 472 if ((addrcell) && (*addrcell == 2)) { 473 tmp[0] = (start >> 56) & 0xff; 474 tmp[1] = (start >> 48) & 0xff; 475 tmp[2] = (start >> 40) & 0xff; 476 tmp[3] = (start >> 32) & 0xff; 477 tmp[4] = (start >> 24) & 0xff; 478 tmp[5] = (start >> 16) & 0xff; 479 tmp[6] = (start >> 8) & 0xff; 480 tmp[7] = (start ) & 0xff; 481 len = 8; 482 } else { 483 tmp[0] = (start >> 24) & 0xff; 484 tmp[1] = (start >> 16) & 0xff; 485 tmp[2] = (start >> 8) & 0xff; 486 tmp[3] = (start ) & 0xff; 487 len = 4; 488 } 489 490 sizecell = fdt_getprop(blob, 0, "#size-cells", NULL); 491 /* use shifts and mask to ensure endianness */ 492 if ((sizecell) && (*sizecell == 2)) { 493 tmp[0+len] = (size >> 56) & 0xff; 494 tmp[1+len] = (size >> 48) & 0xff; 495 tmp[2+len] = (size >> 40) & 0xff; 496 tmp[3+len] = (size >> 32) & 0xff; 497 tmp[4+len] = (size >> 24) & 0xff; 498 tmp[5+len] = (size >> 16) & 0xff; 499 tmp[6+len] = (size >> 8) & 0xff; 500 tmp[7+len] = (size ) & 0xff; 501 len += 8; 502 } else { 503 tmp[0+len] = (size >> 24) & 0xff; 504 tmp[1+len] = (size >> 16) & 0xff; 505 tmp[2+len] = (size >> 8) & 0xff; 506 tmp[3+len] = (size ) & 0xff; 507 len += 4; 508 } 509 510 err = fdt_setprop(blob, nodeoffset, "reg", tmp, len); 511 if (err < 0) { 512 printf("WARNING: could not set %s %s.\n", 513 "reg", fdt_strerror(err)); 514 return err; 515 } 516 return 0; 517 } 518 519 void fdt_fixup_ethernet(void *fdt, bd_t *bd) 520 { 521 int node; 522 const char *path; 523 524 node = fdt_path_offset(fdt, "/aliases"); 525 if (node >= 0) { 526 #if defined(CONFIG_HAS_ETH0) 527 path = fdt_getprop(fdt, node, "ethernet0", NULL); 528 if (path) { 529 do_fixup_by_path(fdt, path, "mac-address", 530 bd->bi_enetaddr, 6, 0); 531 do_fixup_by_path(fdt, path, "local-mac-address", 532 bd->bi_enetaddr, 6, 1); 533 } 534 #endif 535 #if defined(CONFIG_HAS_ETH1) 536 path = fdt_getprop(fdt, node, "ethernet1", NULL); 537 if (path) { 538 do_fixup_by_path(fdt, path, "mac-address", 539 bd->bi_enet1addr, 6, 0); 540 do_fixup_by_path(fdt, path, "local-mac-address", 541 bd->bi_enet1addr, 6, 1); 542 } 543 #endif 544 #if defined(CONFIG_HAS_ETH2) 545 path = fdt_getprop(fdt, node, "ethernet2", NULL); 546 if (path) { 547 do_fixup_by_path(fdt, path, "mac-address", 548 bd->bi_enet2addr, 6, 0); 549 do_fixup_by_path(fdt, path, "local-mac-address", 550 bd->bi_enet2addr, 6, 1); 551 } 552 #endif 553 #if defined(CONFIG_HAS_ETH3) 554 path = fdt_getprop(fdt, node, "ethernet3", NULL); 555 if (path) { 556 do_fixup_by_path(fdt, path, "mac-address", 557 bd->bi_enet3addr, 6, 0); 558 do_fixup_by_path(fdt, path, "local-mac-address", 559 bd->bi_enet3addr, 6, 1); 560 } 561 #endif 562 } 563 } 564