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 * 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 #ifdef CONFIG_OF_STDOUT_VIA_ALIAS 71 static int fdt_fixup_stdout(void *fdt, int choosenoff) 72 { 73 int err = 0; 74 #ifdef CONFIG_CONS_INDEX 75 int node; 76 char sername[9] = { 0 }; 77 const char *path; 78 79 sprintf(sername, "serial%d", CONFIG_CONS_INDEX - 1); 80 81 err = node = fdt_path_offset(fdt, "/aliases"); 82 if (node >= 0) { 83 int len; 84 path = fdt_getprop(fdt, node, sername, &len); 85 if (path) { 86 char *p = malloc(len); 87 err = -FDT_ERR_NOSPACE; 88 if (p) { 89 memcpy(p, path, len); 90 err = fdt_setprop(fdt, choosenoff, 91 "linux,stdout-path", p, len); 92 free(p); 93 } 94 } else { 95 err = len; 96 } 97 } 98 #endif 99 if (err < 0) 100 printf("WARNING: could not set linux,stdout-path %s.\n", 101 fdt_strerror(err)); 102 103 return err; 104 } 105 #endif 106 107 int fdt_chosen(void *fdt, ulong initrd_start, ulong initrd_end, int force) 108 { 109 int nodeoffset; 110 int err; 111 u32 tmp; /* used to set 32 bit integer properties */ 112 char *str; /* used to set string properties */ 113 const char *path; 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 there is no "chosen" node in the blob, create it. 152 */ 153 if (nodeoffset < 0) { 154 /* 155 * Create a new node "/chosen" (offset 0 is root level) 156 */ 157 nodeoffset = fdt_add_subnode(fdt, 0, "chosen"); 158 if (nodeoffset < 0) { 159 printf("WARNING: could not create /chosen %s.\n", 160 fdt_strerror(nodeoffset)); 161 return nodeoffset; 162 } 163 } 164 165 /* 166 * Create /chosen properites that don't exist in the fdt. 167 * If the property exists, update it only if the "force" parameter 168 * is true. 169 */ 170 str = getenv("bootargs"); 171 if (str != NULL) { 172 path = fdt_getprop(fdt, nodeoffset, "bootargs", NULL); 173 if ((path == NULL) || force) { 174 err = fdt_setprop(fdt, nodeoffset, 175 "bootargs", str, strlen(str)+1); 176 if (err < 0) 177 printf("WARNING: could not set bootargs %s.\n", 178 fdt_strerror(err)); 179 } 180 } 181 if (initrd_start && initrd_end) { 182 path = fdt_getprop(fdt, nodeoffset, "linux,initrd-start", NULL); 183 if ((path == NULL) || force) { 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 200 #ifdef CONFIG_OF_STDOUT_VIA_ALIAS 201 path = fdt_getprop(fdt, nodeoffset, "linux,stdout-path", NULL); 202 if ((path == NULL) || force) 203 err = fdt_fixup_stdout(fdt, nodeoffset); 204 #endif 205 206 #ifdef OF_STDOUT_PATH 207 path = fdt_getprop(fdt, nodeoffset, "linux,stdout-path", NULL); 208 if ((path == NULL) || force) { 209 err = fdt_setprop(fdt, nodeoffset, 210 "linux,stdout-path", OF_STDOUT_PATH, strlen(OF_STDOUT_PATH)+1); 211 if (err < 0) 212 printf("WARNING: could not set linux,stdout-path %s.\n", 213 fdt_strerror(err)); 214 } 215 #endif 216 217 return err; 218 } 219 220 void do_fixup_by_path(void *fdt, const char *path, const char *prop, 221 const void *val, int len, int create) 222 { 223 #if defined(DEBUG) 224 int i; 225 debug("Updating property '%s/%s' = ", path, prop); 226 for (i = 0; i < len; i++) 227 debug(" %.2x", *(u8*)(val+i)); 228 debug("\n"); 229 #endif 230 int rc = fdt_find_and_setprop(fdt, path, prop, val, len, create); 231 if (rc) 232 printf("Unable to update property %s:%s, err=%s\n", 233 path, prop, fdt_strerror(rc)); 234 } 235 236 void do_fixup_by_path_u32(void *fdt, const char *path, const char *prop, 237 u32 val, int create) 238 { 239 val = cpu_to_fdt32(val); 240 do_fixup_by_path(fdt, path, prop, &val, sizeof(val), create); 241 } 242 243 void do_fixup_by_prop(void *fdt, 244 const char *pname, const void *pval, int plen, 245 const char *prop, const void *val, int len, 246 int create) 247 { 248 int off; 249 #if defined(DEBUG) 250 int i; 251 debug("Updating property '%s' = ", prop); 252 for (i = 0; i < len; i++) 253 debug(" %.2x", *(u8*)(val+i)); 254 debug("\n"); 255 #endif 256 off = fdt_node_offset_by_prop_value(fdt, -1, pname, pval, plen); 257 while (off != -FDT_ERR_NOTFOUND) { 258 if (create || (fdt_get_property(fdt, off, prop, 0) != NULL)) 259 fdt_setprop(fdt, off, prop, val, len); 260 off = fdt_node_offset_by_prop_value(fdt, off, pname, pval, plen); 261 } 262 } 263 264 void do_fixup_by_prop_u32(void *fdt, 265 const char *pname, const void *pval, int plen, 266 const char *prop, u32 val, int create) 267 { 268 val = cpu_to_fdt32(val); 269 do_fixup_by_prop(fdt, pname, pval, plen, prop, &val, 4, create); 270 } 271 272 void do_fixup_by_compat(void *fdt, const char *compat, 273 const char *prop, const void *val, int len, int create) 274 { 275 int off = -1; 276 #if defined(DEBUG) 277 int i; 278 debug("Updating property '%s' = ", prop); 279 for (i = 0; i < len; i++) 280 debug(" %.2x", *(u8*)(val+i)); 281 debug("\n"); 282 #endif 283 off = fdt_node_offset_by_compatible(fdt, -1, compat); 284 while (off != -FDT_ERR_NOTFOUND) { 285 if (create || (fdt_get_property(fdt, off, prop, 0) != NULL)) 286 fdt_setprop(fdt, off, prop, val, len); 287 off = fdt_node_offset_by_compatible(fdt, off, compat); 288 } 289 } 290 291 void do_fixup_by_compat_u32(void *fdt, const char *compat, 292 const char *prop, u32 val, int create) 293 { 294 val = cpu_to_fdt32(val); 295 do_fixup_by_compat(fdt, compat, prop, &val, 4, create); 296 } 297 298 int fdt_fixup_memory(void *blob, u64 start, u64 size) 299 { 300 int err, nodeoffset, len = 0; 301 u8 tmp[16]; 302 const u32 *addrcell, *sizecell; 303 304 err = fdt_check_header(blob); 305 if (err < 0) { 306 printf("%s: %s\n", __FUNCTION__, fdt_strerror(err)); 307 return err; 308 } 309 310 /* update, or add and update /memory node */ 311 nodeoffset = fdt_path_offset(blob, "/memory"); 312 if (nodeoffset < 0) { 313 nodeoffset = fdt_add_subnode(blob, 0, "memory"); 314 if (nodeoffset < 0) 315 printf("WARNING: could not create /memory: %s.\n", 316 fdt_strerror(nodeoffset)); 317 return nodeoffset; 318 } 319 err = fdt_setprop(blob, nodeoffset, "device_type", "memory", 320 sizeof("memory")); 321 if (err < 0) { 322 printf("WARNING: could not set %s %s.\n", "device_type", 323 fdt_strerror(err)); 324 return err; 325 } 326 327 addrcell = fdt_getprop(blob, 0, "#address-cells", NULL); 328 /* use shifts and mask to ensure endianness */ 329 if ((addrcell) && (*addrcell == 2)) { 330 tmp[0] = (start >> 56) & 0xff; 331 tmp[1] = (start >> 48) & 0xff; 332 tmp[2] = (start >> 40) & 0xff; 333 tmp[3] = (start >> 32) & 0xff; 334 tmp[4] = (start >> 24) & 0xff; 335 tmp[5] = (start >> 16) & 0xff; 336 tmp[6] = (start >> 8) & 0xff; 337 tmp[7] = (start ) & 0xff; 338 len = 8; 339 } else { 340 tmp[0] = (start >> 24) & 0xff; 341 tmp[1] = (start >> 16) & 0xff; 342 tmp[2] = (start >> 8) & 0xff; 343 tmp[3] = (start ) & 0xff; 344 len = 4; 345 } 346 347 sizecell = fdt_getprop(blob, 0, "#size-cells", NULL); 348 /* use shifts and mask to ensure endianness */ 349 if ((sizecell) && (*sizecell == 2)) { 350 tmp[0+len] = (size >> 56) & 0xff; 351 tmp[1+len] = (size >> 48) & 0xff; 352 tmp[2+len] = (size >> 40) & 0xff; 353 tmp[3+len] = (size >> 32) & 0xff; 354 tmp[4+len] = (size >> 24) & 0xff; 355 tmp[5+len] = (size >> 16) & 0xff; 356 tmp[6+len] = (size >> 8) & 0xff; 357 tmp[7+len] = (size ) & 0xff; 358 len += 8; 359 } else { 360 tmp[0+len] = (size >> 24) & 0xff; 361 tmp[1+len] = (size >> 16) & 0xff; 362 tmp[2+len] = (size >> 8) & 0xff; 363 tmp[3+len] = (size ) & 0xff; 364 len += 4; 365 } 366 367 err = fdt_setprop(blob, nodeoffset, "reg", tmp, len); 368 if (err < 0) { 369 printf("WARNING: could not set %s %s.\n", 370 "reg", fdt_strerror(err)); 371 return err; 372 } 373 return 0; 374 } 375 376 #if defined(CONFIG_HAS_ETH0) || defined(CONFIG_HAS_ETH1) ||\ 377 defined(CONFIG_HAS_ETH2) || defined(CONFIG_HAS_ETH3) 378 379 void fdt_fixup_ethernet(void *fdt, bd_t *bd) 380 { 381 int node; 382 const char *path; 383 384 node = fdt_path_offset(fdt, "/aliases"); 385 if (node >= 0) { 386 #if defined(CONFIG_HAS_ETH0) 387 path = fdt_getprop(fdt, node, "ethernet0", NULL); 388 if (path) { 389 do_fixup_by_path(fdt, path, "mac-address", 390 bd->bi_enetaddr, 6, 0); 391 do_fixup_by_path(fdt, path, "local-mac-address", 392 bd->bi_enetaddr, 6, 1); 393 } 394 #endif 395 #if defined(CONFIG_HAS_ETH1) 396 path = fdt_getprop(fdt, node, "ethernet1", NULL); 397 if (path) { 398 do_fixup_by_path(fdt, path, "mac-address", 399 bd->bi_enet1addr, 6, 0); 400 do_fixup_by_path(fdt, path, "local-mac-address", 401 bd->bi_enet1addr, 6, 1); 402 } 403 #endif 404 #if defined(CONFIG_HAS_ETH2) 405 path = fdt_getprop(fdt, node, "ethernet2", NULL); 406 if (path) { 407 do_fixup_by_path(fdt, path, "mac-address", 408 bd->bi_enet2addr, 6, 0); 409 do_fixup_by_path(fdt, path, "local-mac-address", 410 bd->bi_enet2addr, 6, 1); 411 } 412 #endif 413 #if defined(CONFIG_HAS_ETH3) 414 path = fdt_getprop(fdt, node, "ethernet3", NULL); 415 if (path) { 416 do_fixup_by_path(fdt, path, "mac-address", 417 bd->bi_enet3addr, 6, 0); 418 do_fixup_by_path(fdt, path, "local-mac-address", 419 bd->bi_enet3addr, 6, 1); 420 } 421 #endif 422 } 423 } 424 #endif 425 426 #ifdef CONFIG_HAS_FSL_DR_USB 427 void fdt_fixup_dr_usb(void *blob, bd_t *bd) 428 { 429 char *mode; 430 const char *compat = "fsl-usb2-dr"; 431 const char *prop = "dr_mode"; 432 int node_offset; 433 int err; 434 435 mode = getenv("usb_dr_mode"); 436 if (!mode) 437 return; 438 439 node_offset = fdt_node_offset_by_compatible(blob, 0, compat); 440 if (node_offset < 0) 441 printf("WARNING: could not find compatible node %s: %s.\n", 442 compat, fdt_strerror(node_offset)); 443 444 err = fdt_setprop(blob, node_offset, prop, mode, strlen(mode) + 1); 445 if (err < 0) 446 printf("WARNING: could not set %s for %s: %s.\n", 447 prop, compat, fdt_strerror(err)); 448 } 449 #endif /* CONFIG_HAS_FSL_DR_USB */ 450