164dbbd40SGerald Van Baren /* 264dbbd40SGerald Van Baren * (C) Copyright 2007 364dbbd40SGerald Van Baren * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com 464dbbd40SGerald Van Baren * 5a0342c08SKumar Gala * Copyright 2010 Freescale Semiconductor, Inc. 6a0342c08SKumar Gala * 764dbbd40SGerald Van Baren * See file CREDITS for list of people who contributed to this 864dbbd40SGerald Van Baren * project. 964dbbd40SGerald Van Baren * 1064dbbd40SGerald Van Baren * This program is free software; you can redistribute it and/or 1164dbbd40SGerald Van Baren * modify it under the terms of the GNU General Public License as 1264dbbd40SGerald Van Baren * published by the Free Software Foundation; either version 2 of 1364dbbd40SGerald Van Baren * the License, or (at your option) any later version. 1464dbbd40SGerald Van Baren * 1564dbbd40SGerald Van Baren * This program is distributed in the hope that it will be useful, 1664dbbd40SGerald Van Baren * but WITHOUT ANY WARRANTY; without even the implied warranty of 1764dbbd40SGerald Van Baren * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1864dbbd40SGerald Van Baren * GNU General Public License for more details. 1964dbbd40SGerald Van Baren * 2064dbbd40SGerald Van Baren * You should have received a copy of the GNU General Public License 2164dbbd40SGerald Van Baren * along with this program; if not, write to the Free Software 2264dbbd40SGerald Van Baren * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 2364dbbd40SGerald Van Baren * MA 02111-1307 USA 2464dbbd40SGerald Van Baren */ 2564dbbd40SGerald Van Baren 2664dbbd40SGerald Van Baren #include <common.h> 273e303f74SAnton Vorontsov #include <stdio_dev.h> 2864dbbd40SGerald Van Baren #include <linux/ctype.h> 2964dbbd40SGerald Van Baren #include <linux/types.h> 3064dbbd40SGerald Van Baren #include <asm/global_data.h> 3164dbbd40SGerald Van Baren #include <fdt.h> 3264dbbd40SGerald Van Baren #include <libfdt.h> 3364dbbd40SGerald Van Baren #include <fdt_support.h> 34151c8b09SKumar Gala #include <exports.h> 3564dbbd40SGerald Van Baren 3664dbbd40SGerald Van Baren /* 3764dbbd40SGerald Van Baren * Global data (for the gd->bd) 3864dbbd40SGerald Van Baren */ 3964dbbd40SGerald Van Baren DECLARE_GLOBAL_DATA_PTR; 4064dbbd40SGerald Van Baren 413bed2aafSKumar Gala /** 423bed2aafSKumar Gala * fdt_getprop_u32_default - Find a node and return it's property or a default 433bed2aafSKumar Gala * 443bed2aafSKumar Gala * @fdt: ptr to device tree 453bed2aafSKumar Gala * @path: path of node 463bed2aafSKumar Gala * @prop: property name 473bed2aafSKumar Gala * @dflt: default value if the property isn't found 483bed2aafSKumar Gala * 493bed2aafSKumar Gala * Convenience function to find a node and return it's property or a 503bed2aafSKumar Gala * default value if it doesn't exist. 513bed2aafSKumar Gala */ 523bed2aafSKumar Gala u32 fdt_getprop_u32_default(void *fdt, const char *path, const char *prop, 533bed2aafSKumar Gala const u32 dflt) 543bed2aafSKumar Gala { 553bed2aafSKumar Gala const u32 *val; 563bed2aafSKumar Gala int off; 573bed2aafSKumar Gala 583bed2aafSKumar Gala off = fdt_path_offset(fdt, path); 593bed2aafSKumar Gala if (off < 0) 603bed2aafSKumar Gala return dflt; 613bed2aafSKumar Gala 623bed2aafSKumar Gala val = fdt_getprop(fdt, off, prop, NULL); 633bed2aafSKumar Gala if (val) 643bed2aafSKumar Gala return *val; 653bed2aafSKumar Gala else 663bed2aafSKumar Gala return dflt; 673bed2aafSKumar Gala } 6864dbbd40SGerald Van Baren 69a3c2933eSKumar Gala /** 70a3c2933eSKumar Gala * fdt_find_and_setprop: Find a node and set it's property 71a3c2933eSKumar Gala * 72a3c2933eSKumar Gala * @fdt: ptr to device tree 73a3c2933eSKumar Gala * @node: path of node 74a3c2933eSKumar Gala * @prop: property name 75a3c2933eSKumar Gala * @val: ptr to new value 76a3c2933eSKumar Gala * @len: length of new property value 77a3c2933eSKumar Gala * @create: flag to create the property if it doesn't exist 78a3c2933eSKumar Gala * 79a3c2933eSKumar Gala * Convenience function to directly set a property given the path to the node. 80a3c2933eSKumar Gala */ 81a3c2933eSKumar Gala int fdt_find_and_setprop(void *fdt, const char *node, const char *prop, 82a3c2933eSKumar Gala const void *val, int len, int create) 83a3c2933eSKumar Gala { 848d04f02fSKumar Gala int nodeoff = fdt_path_offset(fdt, node); 85a3c2933eSKumar Gala 86a3c2933eSKumar Gala if (nodeoff < 0) 87a3c2933eSKumar Gala return nodeoff; 88a3c2933eSKumar Gala 89a3c2933eSKumar Gala if ((!create) && (fdt_get_property(fdt, nodeoff, prop, 0) == NULL)) 90a3c2933eSKumar Gala return 0; /* create flag not set; so exit quietly */ 91a3c2933eSKumar Gala 92a3c2933eSKumar Gala return fdt_setprop(fdt, nodeoff, prop, val, len); 93a3c2933eSKumar Gala } 94a3c2933eSKumar Gala 95151c8b09SKumar Gala #ifdef CONFIG_OF_STDOUT_VIA_ALIAS 963e303f74SAnton Vorontsov 973e303f74SAnton Vorontsov #ifdef CONFIG_SERIAL_MULTI 983e303f74SAnton Vorontsov static void fdt_fill_multisername(char *sername, size_t maxlen) 993e303f74SAnton Vorontsov { 1003e303f74SAnton Vorontsov const char *outname = stdio_devices[stdout]->name; 1013e303f74SAnton Vorontsov 1023e303f74SAnton Vorontsov if (strcmp(outname, "serial") > 0) 1033e303f74SAnton Vorontsov strncpy(sername, outname, maxlen); 1043e303f74SAnton Vorontsov 1053e303f74SAnton Vorontsov /* eserial? */ 1063e303f74SAnton Vorontsov if (strcmp(outname + 1, "serial") > 0) 1073e303f74SAnton Vorontsov strncpy(sername, outname + 1, maxlen); 1083e303f74SAnton Vorontsov } 1093e303f74SAnton Vorontsov #else 1103e303f74SAnton Vorontsov static inline void fdt_fill_multisername(char *sername, size_t maxlen) {} 1113e303f74SAnton Vorontsov #endif /* CONFIG_SERIAL_MULTI */ 1123e303f74SAnton Vorontsov 11340777812SDetlev Zundel static int fdt_fixup_stdout(void *fdt, int chosenoff) 114151c8b09SKumar Gala { 115151c8b09SKumar Gala int err = 0; 116151c8b09SKumar Gala #ifdef CONFIG_CONS_INDEX 117151c8b09SKumar Gala int node; 118151c8b09SKumar Gala char sername[9] = { 0 }; 119151c8b09SKumar Gala const char *path; 120151c8b09SKumar Gala 1213e303f74SAnton Vorontsov fdt_fill_multisername(sername, sizeof(sername) - 1); 1223e303f74SAnton Vorontsov if (!sername[0]) 123151c8b09SKumar Gala sprintf(sername, "serial%d", CONFIG_CONS_INDEX - 1); 124151c8b09SKumar Gala 125151c8b09SKumar Gala err = node = fdt_path_offset(fdt, "/aliases"); 126151c8b09SKumar Gala if (node >= 0) { 127151c8b09SKumar Gala int len; 128151c8b09SKumar Gala path = fdt_getprop(fdt, node, sername, &len); 129151c8b09SKumar Gala if (path) { 130151c8b09SKumar Gala char *p = malloc(len); 131151c8b09SKumar Gala err = -FDT_ERR_NOSPACE; 132151c8b09SKumar Gala if (p) { 133151c8b09SKumar Gala memcpy(p, path, len); 13440777812SDetlev Zundel err = fdt_setprop(fdt, chosenoff, 135151c8b09SKumar Gala "linux,stdout-path", p, len); 136151c8b09SKumar Gala free(p); 137151c8b09SKumar Gala } 138151c8b09SKumar Gala } else { 139151c8b09SKumar Gala err = len; 140151c8b09SKumar Gala } 141151c8b09SKumar Gala } 142151c8b09SKumar Gala #endif 143151c8b09SKumar Gala if (err < 0) 144151c8b09SKumar Gala printf("WARNING: could not set linux,stdout-path %s.\n", 145151c8b09SKumar Gala fdt_strerror(err)); 146151c8b09SKumar Gala 147151c8b09SKumar Gala return err; 148151c8b09SKumar Gala } 149151c8b09SKumar Gala #endif 150151c8b09SKumar Gala 1512a1a2cb6SKumar Gala int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end, int force) 15264dbbd40SGerald Van Baren { 15364dbbd40SGerald Van Baren int nodeoffset; 1542a1a2cb6SKumar Gala int err, j, total; 1552a1a2cb6SKumar Gala u32 tmp; 156b60af3d4SGerald Van Baren const char *path; 1572a1a2cb6SKumar Gala uint64_t addr, size; 15864dbbd40SGerald Van Baren 1592a1a2cb6SKumar Gala /* Find the "chosen" node. */ 1602a1a2cb6SKumar Gala nodeoffset = fdt_path_offset (fdt, "/chosen"); 1612a1a2cb6SKumar Gala 1622a1a2cb6SKumar Gala /* If there is no "chosen" node in the blob return */ 1632a1a2cb6SKumar Gala if (nodeoffset < 0) { 1642a1a2cb6SKumar Gala printf("fdt_initrd: %s\n", fdt_strerror(nodeoffset)); 1652a1a2cb6SKumar Gala return nodeoffset; 16664dbbd40SGerald Van Baren } 16764dbbd40SGerald Van Baren 1682a1a2cb6SKumar Gala /* just return if initrd_start/end aren't valid */ 1692a1a2cb6SKumar Gala if ((initrd_start == 0) || (initrd_end == 0)) 1702a1a2cb6SKumar Gala return 0; 1712a1a2cb6SKumar Gala 1722a1a2cb6SKumar Gala total = fdt_num_mem_rsv(fdt); 173c28abb9cSGerald Van Baren 174c28abb9cSGerald Van Baren /* 175c28abb9cSGerald Van Baren * Look for an existing entry and update it. If we don't find 176c28abb9cSGerald Van Baren * the entry, we will j be the next available slot. 177c28abb9cSGerald Van Baren */ 1788d04f02fSKumar Gala for (j = 0; j < total; j++) { 1798d04f02fSKumar Gala err = fdt_get_mem_rsv(fdt, j, &addr, &size); 1808d04f02fSKumar Gala if (addr == initrd_start) { 1818d04f02fSKumar Gala fdt_del_mem_rsv(fdt, j); 182c28abb9cSGerald Van Baren break; 183c28abb9cSGerald Van Baren } 184c28abb9cSGerald Van Baren } 1858d04f02fSKumar Gala 1868d04f02fSKumar Gala err = fdt_add_mem_rsv(fdt, initrd_start, initrd_end - initrd_start + 1); 18764dbbd40SGerald Van Baren if (err < 0) { 1882a1a2cb6SKumar Gala printf("fdt_initrd: %s\n", fdt_strerror(err)); 18964dbbd40SGerald Van Baren return err; 19064dbbd40SGerald Van Baren } 1912a1a2cb6SKumar Gala 1922a1a2cb6SKumar Gala path = fdt_getprop(fdt, nodeoffset, "linux,initrd-start", NULL); 1932a1a2cb6SKumar Gala if ((path == NULL) || force) { 1942a1a2cb6SKumar Gala tmp = __cpu_to_be32(initrd_start); 1952a1a2cb6SKumar Gala err = fdt_setprop(fdt, nodeoffset, 1962a1a2cb6SKumar Gala "linux,initrd-start", &tmp, sizeof(tmp)); 1972a1a2cb6SKumar Gala if (err < 0) { 1982a1a2cb6SKumar Gala printf("WARNING: " 1992a1a2cb6SKumar Gala "could not set linux,initrd-start %s.\n", 2002a1a2cb6SKumar Gala fdt_strerror(err)); 2012a1a2cb6SKumar Gala return err; 2022a1a2cb6SKumar Gala } 2032a1a2cb6SKumar Gala tmp = __cpu_to_be32(initrd_end); 2042a1a2cb6SKumar Gala err = fdt_setprop(fdt, nodeoffset, 2052a1a2cb6SKumar Gala "linux,initrd-end", &tmp, sizeof(tmp)); 2062a1a2cb6SKumar Gala if (err < 0) { 2072a1a2cb6SKumar Gala printf("WARNING: could not set linux,initrd-end %s.\n", 2082a1a2cb6SKumar Gala fdt_strerror(err)); 2092a1a2cb6SKumar Gala 2102a1a2cb6SKumar Gala return err; 2112a1a2cb6SKumar Gala } 2122a1a2cb6SKumar Gala } 2132a1a2cb6SKumar Gala 2142a1a2cb6SKumar Gala return 0; 2152a1a2cb6SKumar Gala } 2162a1a2cb6SKumar Gala 21756844a22SHeiko Schocher int fdt_chosen(void *fdt, int force) 2182a1a2cb6SKumar Gala { 2192a1a2cb6SKumar Gala int nodeoffset; 2202a1a2cb6SKumar Gala int err; 2212a1a2cb6SKumar Gala char *str; /* used to set string properties */ 2222a1a2cb6SKumar Gala const char *path; 2232a1a2cb6SKumar Gala 2242a1a2cb6SKumar Gala err = fdt_check_header(fdt); 2252a1a2cb6SKumar Gala if (err < 0) { 2262a1a2cb6SKumar Gala printf("fdt_chosen: %s\n", fdt_strerror(err)); 2272a1a2cb6SKumar Gala return err; 22864dbbd40SGerald Van Baren } 22964dbbd40SGerald Van Baren 23064dbbd40SGerald Van Baren /* 23164dbbd40SGerald Van Baren * Find the "chosen" node. 23264dbbd40SGerald Van Baren */ 2338d04f02fSKumar Gala nodeoffset = fdt_path_offset (fdt, "/chosen"); 23464dbbd40SGerald Van Baren 23564dbbd40SGerald Van Baren /* 236b60af3d4SGerald Van Baren * If there is no "chosen" node in the blob, create it. 23764dbbd40SGerald Van Baren */ 23864dbbd40SGerald Van Baren if (nodeoffset < 0) { 23964dbbd40SGerald Van Baren /* 24064dbbd40SGerald Van Baren * Create a new node "/chosen" (offset 0 is root level) 24164dbbd40SGerald Van Baren */ 24264dbbd40SGerald Van Baren nodeoffset = fdt_add_subnode(fdt, 0, "chosen"); 24364dbbd40SGerald Van Baren if (nodeoffset < 0) { 2445fe6be62SGerald Van Baren printf("WARNING: could not create /chosen %s.\n", 24535ec398fSGerald Van Baren fdt_strerror(nodeoffset)); 24664dbbd40SGerald Van Baren return nodeoffset; 24764dbbd40SGerald Van Baren } 24864dbbd40SGerald Van Baren } 24964dbbd40SGerald Van Baren 25064dbbd40SGerald Van Baren /* 251b60af3d4SGerald Van Baren * Create /chosen properites that don't exist in the fdt. 252b60af3d4SGerald Van Baren * If the property exists, update it only if the "force" parameter 253b60af3d4SGerald Van Baren * is true. 25464dbbd40SGerald Van Baren */ 25564dbbd40SGerald Van Baren str = getenv("bootargs"); 25664dbbd40SGerald Van Baren if (str != NULL) { 257b60af3d4SGerald Van Baren path = fdt_getprop(fdt, nodeoffset, "bootargs", NULL); 258b60af3d4SGerald Van Baren if ((path == NULL) || force) { 25935ec398fSGerald Van Baren err = fdt_setprop(fdt, nodeoffset, 26035ec398fSGerald Van Baren "bootargs", str, strlen(str)+1); 26164dbbd40SGerald Van Baren if (err < 0) 2625fe6be62SGerald Van Baren printf("WARNING: could not set bootargs %s.\n", 26335ec398fSGerald Van Baren fdt_strerror(err)); 26464dbbd40SGerald Van Baren } 265b60af3d4SGerald Van Baren } 2662a1a2cb6SKumar Gala 267151c8b09SKumar Gala #ifdef CONFIG_OF_STDOUT_VIA_ALIAS 268b60af3d4SGerald Van Baren path = fdt_getprop(fdt, nodeoffset, "linux,stdout-path", NULL); 269b60af3d4SGerald Van Baren if ((path == NULL) || force) 270151c8b09SKumar Gala err = fdt_fixup_stdout(fdt, nodeoffset); 271151c8b09SKumar Gala #endif 272151c8b09SKumar Gala 27364dbbd40SGerald Van Baren #ifdef OF_STDOUT_PATH 274b60af3d4SGerald Van Baren path = fdt_getprop(fdt, nodeoffset, "linux,stdout-path", NULL); 275b60af3d4SGerald Van Baren if ((path == NULL) || force) { 27635ec398fSGerald Van Baren err = fdt_setprop(fdt, nodeoffset, 27735ec398fSGerald Van Baren "linux,stdout-path", OF_STDOUT_PATH, strlen(OF_STDOUT_PATH)+1); 27864dbbd40SGerald Van Baren if (err < 0) 2795fe6be62SGerald Van Baren printf("WARNING: could not set linux,stdout-path %s.\n", 28035ec398fSGerald Van Baren fdt_strerror(err)); 281b60af3d4SGerald Van Baren } 28264dbbd40SGerald Van Baren #endif 28364dbbd40SGerald Van Baren 28464dbbd40SGerald Van Baren return err; 28564dbbd40SGerald Van Baren } 28664dbbd40SGerald Van Baren 287e93becf8SKumar Gala void do_fixup_by_path(void *fdt, const char *path, const char *prop, 288e93becf8SKumar Gala const void *val, int len, int create) 289e93becf8SKumar Gala { 290e93becf8SKumar Gala #if defined(DEBUG) 291e93becf8SKumar Gala int i; 292d9ad115bSKumar Gala debug("Updating property '%s/%s' = ", path, prop); 293e93becf8SKumar Gala for (i = 0; i < len; i++) 294e93becf8SKumar Gala debug(" %.2x", *(u8*)(val+i)); 295e93becf8SKumar Gala debug("\n"); 296e93becf8SKumar Gala #endif 297e93becf8SKumar Gala int rc = fdt_find_and_setprop(fdt, path, prop, val, len, create); 298e93becf8SKumar Gala if (rc) 299e93becf8SKumar Gala printf("Unable to update property %s:%s, err=%s\n", 300e93becf8SKumar Gala path, prop, fdt_strerror(rc)); 301e93becf8SKumar Gala } 302e93becf8SKumar Gala 303e93becf8SKumar Gala void do_fixup_by_path_u32(void *fdt, const char *path, const char *prop, 304e93becf8SKumar Gala u32 val, int create) 305e93becf8SKumar Gala { 306e93becf8SKumar Gala val = cpu_to_fdt32(val); 307e93becf8SKumar Gala do_fixup_by_path(fdt, path, prop, &val, sizeof(val), create); 308e93becf8SKumar Gala } 309e93becf8SKumar Gala 3109eb77ceaSKumar Gala void do_fixup_by_prop(void *fdt, 3119eb77ceaSKumar Gala const char *pname, const void *pval, int plen, 3129eb77ceaSKumar Gala const char *prop, const void *val, int len, 3139eb77ceaSKumar Gala int create) 3149eb77ceaSKumar Gala { 3159eb77ceaSKumar Gala int off; 3169eb77ceaSKumar Gala #if defined(DEBUG) 3179eb77ceaSKumar Gala int i; 318d9ad115bSKumar Gala debug("Updating property '%s' = ", prop); 3199eb77ceaSKumar Gala for (i = 0; i < len; i++) 3209eb77ceaSKumar Gala debug(" %.2x", *(u8*)(val+i)); 3219eb77ceaSKumar Gala debug("\n"); 3229eb77ceaSKumar Gala #endif 3239eb77ceaSKumar Gala off = fdt_node_offset_by_prop_value(fdt, -1, pname, pval, plen); 3249eb77ceaSKumar Gala while (off != -FDT_ERR_NOTFOUND) { 3259eb77ceaSKumar Gala if (create || (fdt_get_property(fdt, off, prop, 0) != NULL)) 3269eb77ceaSKumar Gala fdt_setprop(fdt, off, prop, val, len); 3279eb77ceaSKumar Gala off = fdt_node_offset_by_prop_value(fdt, off, pname, pval, plen); 3289eb77ceaSKumar Gala } 3299eb77ceaSKumar Gala } 3309eb77ceaSKumar Gala 3319eb77ceaSKumar Gala void do_fixup_by_prop_u32(void *fdt, 3329eb77ceaSKumar Gala const char *pname, const void *pval, int plen, 3339eb77ceaSKumar Gala const char *prop, u32 val, int create) 3349eb77ceaSKumar Gala { 3359eb77ceaSKumar Gala val = cpu_to_fdt32(val); 3369eb77ceaSKumar Gala do_fixup_by_prop(fdt, pname, pval, plen, prop, &val, 4, create); 3379eb77ceaSKumar Gala } 3389eb77ceaSKumar Gala 3399eb77ceaSKumar Gala void do_fixup_by_compat(void *fdt, const char *compat, 3409eb77ceaSKumar Gala const char *prop, const void *val, int len, int create) 3419eb77ceaSKumar Gala { 3429eb77ceaSKumar Gala int off = -1; 3439eb77ceaSKumar Gala #if defined(DEBUG) 3449eb77ceaSKumar Gala int i; 345d9ad115bSKumar Gala debug("Updating property '%s' = ", prop); 3469eb77ceaSKumar Gala for (i = 0; i < len; i++) 3479eb77ceaSKumar Gala debug(" %.2x", *(u8*)(val+i)); 3489eb77ceaSKumar Gala debug("\n"); 3499eb77ceaSKumar Gala #endif 3509eb77ceaSKumar Gala off = fdt_node_offset_by_compatible(fdt, -1, compat); 3519eb77ceaSKumar Gala while (off != -FDT_ERR_NOTFOUND) { 3529eb77ceaSKumar Gala if (create || (fdt_get_property(fdt, off, prop, 0) != NULL)) 3539eb77ceaSKumar Gala fdt_setprop(fdt, off, prop, val, len); 3549eb77ceaSKumar Gala off = fdt_node_offset_by_compatible(fdt, off, compat); 3559eb77ceaSKumar Gala } 3569eb77ceaSKumar Gala } 3579eb77ceaSKumar Gala 3589eb77ceaSKumar Gala void do_fixup_by_compat_u32(void *fdt, const char *compat, 3599eb77ceaSKumar Gala const char *prop, u32 val, int create) 3609eb77ceaSKumar Gala { 3619eb77ceaSKumar Gala val = cpu_to_fdt32(val); 3629eb77ceaSKumar Gala do_fixup_by_compat(fdt, compat, prop, &val, 4, create); 3639eb77ceaSKumar Gala } 3649eb77ceaSKumar Gala 3653c927281SKumar Gala int fdt_fixup_memory(void *blob, u64 start, u64 size) 3663c927281SKumar Gala { 3673c927281SKumar Gala int err, nodeoffset, len = 0; 3683c927281SKumar Gala u8 tmp[16]; 3693c927281SKumar Gala const u32 *addrcell, *sizecell; 3703c927281SKumar Gala 3713c927281SKumar Gala err = fdt_check_header(blob); 3723c927281SKumar Gala if (err < 0) { 3733c927281SKumar Gala printf("%s: %s\n", __FUNCTION__, fdt_strerror(err)); 3743c927281SKumar Gala return err; 3753c927281SKumar Gala } 3763c927281SKumar Gala 3773c927281SKumar Gala /* update, or add and update /memory node */ 3783c927281SKumar Gala nodeoffset = fdt_path_offset(blob, "/memory"); 3793c927281SKumar Gala if (nodeoffset < 0) { 3803c927281SKumar Gala nodeoffset = fdt_add_subnode(blob, 0, "memory"); 3813c927281SKumar Gala if (nodeoffset < 0) 3823c927281SKumar Gala printf("WARNING: could not create /memory: %s.\n", 3833c927281SKumar Gala fdt_strerror(nodeoffset)); 3843c927281SKumar Gala return nodeoffset; 3853c927281SKumar Gala } 3863c927281SKumar Gala err = fdt_setprop(blob, nodeoffset, "device_type", "memory", 3873c927281SKumar Gala sizeof("memory")); 3883c927281SKumar Gala if (err < 0) { 3893c927281SKumar Gala printf("WARNING: could not set %s %s.\n", "device_type", 3903c927281SKumar Gala fdt_strerror(err)); 3913c927281SKumar Gala return err; 3923c927281SKumar Gala } 3933c927281SKumar Gala 3943c927281SKumar Gala addrcell = fdt_getprop(blob, 0, "#address-cells", NULL); 3953c927281SKumar Gala /* use shifts and mask to ensure endianness */ 3963c927281SKumar Gala if ((addrcell) && (*addrcell == 2)) { 3973c927281SKumar Gala tmp[0] = (start >> 56) & 0xff; 3983c927281SKumar Gala tmp[1] = (start >> 48) & 0xff; 3993c927281SKumar Gala tmp[2] = (start >> 40) & 0xff; 4003c927281SKumar Gala tmp[3] = (start >> 32) & 0xff; 4013c927281SKumar Gala tmp[4] = (start >> 24) & 0xff; 4023c927281SKumar Gala tmp[5] = (start >> 16) & 0xff; 4033c927281SKumar Gala tmp[6] = (start >> 8) & 0xff; 4043c927281SKumar Gala tmp[7] = (start ) & 0xff; 4053c927281SKumar Gala len = 8; 4063c927281SKumar Gala } else { 4073c927281SKumar Gala tmp[0] = (start >> 24) & 0xff; 4083c927281SKumar Gala tmp[1] = (start >> 16) & 0xff; 4093c927281SKumar Gala tmp[2] = (start >> 8) & 0xff; 4103c927281SKumar Gala tmp[3] = (start ) & 0xff; 4113c927281SKumar Gala len = 4; 4123c927281SKumar Gala } 4133c927281SKumar Gala 4143c927281SKumar Gala sizecell = fdt_getprop(blob, 0, "#size-cells", NULL); 4153c927281SKumar Gala /* use shifts and mask to ensure endianness */ 4163c927281SKumar Gala if ((sizecell) && (*sizecell == 2)) { 4173c927281SKumar Gala tmp[0+len] = (size >> 56) & 0xff; 4183c927281SKumar Gala tmp[1+len] = (size >> 48) & 0xff; 4193c927281SKumar Gala tmp[2+len] = (size >> 40) & 0xff; 4203c927281SKumar Gala tmp[3+len] = (size >> 32) & 0xff; 4213c927281SKumar Gala tmp[4+len] = (size >> 24) & 0xff; 4223c927281SKumar Gala tmp[5+len] = (size >> 16) & 0xff; 4233c927281SKumar Gala tmp[6+len] = (size >> 8) & 0xff; 4243c927281SKumar Gala tmp[7+len] = (size ) & 0xff; 4253c927281SKumar Gala len += 8; 4263c927281SKumar Gala } else { 4273c927281SKumar Gala tmp[0+len] = (size >> 24) & 0xff; 4283c927281SKumar Gala tmp[1+len] = (size >> 16) & 0xff; 4293c927281SKumar Gala tmp[2+len] = (size >> 8) & 0xff; 4303c927281SKumar Gala tmp[3+len] = (size ) & 0xff; 4313c927281SKumar Gala len += 4; 4323c927281SKumar Gala } 4333c927281SKumar Gala 4343c927281SKumar Gala err = fdt_setprop(blob, nodeoffset, "reg", tmp, len); 4353c927281SKumar Gala if (err < 0) { 4363c927281SKumar Gala printf("WARNING: could not set %s %s.\n", 4373c927281SKumar Gala "reg", fdt_strerror(err)); 4383c927281SKumar Gala return err; 4393c927281SKumar Gala } 4403c927281SKumar Gala return 0; 4413c927281SKumar Gala } 4423c927281SKumar Gala 443ba37aa03SKumar Gala void fdt_fixup_ethernet(void *fdt) 444ab544633SKumar Gala { 445ba37aa03SKumar Gala int node, i, j; 446ba37aa03SKumar Gala char enet[16], *tmp, *end; 447ba37aa03SKumar Gala char mac[16] = "ethaddr"; 448ab544633SKumar Gala const char *path; 449ba37aa03SKumar Gala unsigned char mac_addr[6]; 450ab544633SKumar Gala 451ab544633SKumar Gala node = fdt_path_offset(fdt, "/aliases"); 452ba37aa03SKumar Gala if (node < 0) 453ba37aa03SKumar Gala return; 454ba37aa03SKumar Gala 455ba37aa03SKumar Gala i = 0; 456ba37aa03SKumar Gala while ((tmp = getenv(mac)) != NULL) { 457ba37aa03SKumar Gala sprintf(enet, "ethernet%d", i); 458ba37aa03SKumar Gala path = fdt_getprop(fdt, node, enet, NULL); 459ba37aa03SKumar Gala if (!path) { 460ba37aa03SKumar Gala debug("No alias for %s\n", enet); 461ba37aa03SKumar Gala sprintf(mac, "eth%daddr", ++i); 462ba37aa03SKumar Gala continue; 463ba37aa03SKumar Gala } 464ba37aa03SKumar Gala 465ba37aa03SKumar Gala for (j = 0; j < 6; j++) { 466ba37aa03SKumar Gala mac_addr[j] = tmp ? simple_strtoul(tmp, &end, 16) : 0; 467ba37aa03SKumar Gala if (tmp) 468ba37aa03SKumar Gala tmp = (*end) ? end+1 : end; 469ba37aa03SKumar Gala } 470ba37aa03SKumar Gala 471ba37aa03SKumar Gala do_fixup_by_path(fdt, path, "mac-address", &mac_addr, 6, 0); 472ab544633SKumar Gala do_fixup_by_path(fdt, path, "local-mac-address", 473ba37aa03SKumar Gala &mac_addr, 6, 1); 474ba37aa03SKumar Gala 475ba37aa03SKumar Gala sprintf(mac, "eth%daddr", ++i); 476ab544633SKumar Gala } 477ab544633SKumar Gala } 47818e69a35SAnton Vorontsov 4793082d234SKumar Gala /* Resize the fdt to its actual size + a bit of padding */ 4803082d234SKumar Gala int fdt_resize(void *blob) 4813082d234SKumar Gala { 4823082d234SKumar Gala int i; 4833082d234SKumar Gala uint64_t addr, size; 4843082d234SKumar Gala int total, ret; 4853082d234SKumar Gala uint actualsize; 4863082d234SKumar Gala 4873082d234SKumar Gala if (!blob) 4883082d234SKumar Gala return 0; 4893082d234SKumar Gala 4903082d234SKumar Gala total = fdt_num_mem_rsv(blob); 4913082d234SKumar Gala for (i = 0; i < total; i++) { 4923082d234SKumar Gala fdt_get_mem_rsv(blob, i, &addr, &size); 4933082d234SKumar Gala if (addr == (uint64_t)(u32)blob) { 4943082d234SKumar Gala fdt_del_mem_rsv(blob, i); 4953082d234SKumar Gala break; 4963082d234SKumar Gala } 4973082d234SKumar Gala } 4983082d234SKumar Gala 499f242a088SPeter Korsgaard /* 500f242a088SPeter Korsgaard * Calculate the actual size of the fdt 5013840ebfaSFeng Wang * plus the size needed for 5 fdt_add_mem_rsv, one 5023840ebfaSFeng Wang * for the fdt itself and 4 for a possible initrd 5033840ebfaSFeng Wang * ((initrd-start + initrd-end) * 2 (name & value)) 504f242a088SPeter Korsgaard */ 5053082d234SKumar Gala actualsize = fdt_off_dt_strings(blob) + 5063840ebfaSFeng Wang fdt_size_dt_strings(blob) + 5 * sizeof(struct fdt_reserve_entry); 5073082d234SKumar Gala 5083082d234SKumar Gala /* Make it so the fdt ends on a page boundary */ 509c088a108SPeter Korsgaard actualsize = ALIGN(actualsize + ((uint)blob & 0xfff), 0x1000); 5103082d234SKumar Gala actualsize = actualsize - ((uint)blob & 0xfff); 5113082d234SKumar Gala 5123082d234SKumar Gala /* Change the fdt header to reflect the correct size */ 5133082d234SKumar Gala fdt_set_totalsize(blob, actualsize); 5143082d234SKumar Gala 5153082d234SKumar Gala /* Add the new reservation */ 5163082d234SKumar Gala ret = fdt_add_mem_rsv(blob, (uint)blob, actualsize); 5173082d234SKumar Gala if (ret < 0) 5183082d234SKumar Gala return ret; 5193082d234SKumar Gala 5203082d234SKumar Gala return actualsize; 5213082d234SKumar Gala } 5228ab451c4SKumar Gala 5238ab451c4SKumar Gala #ifdef CONFIG_PCI 524cfd700beSKumar Gala #define CONFIG_SYS_PCI_NR_INBOUND_WIN 4 5258ab451c4SKumar Gala 5268ab451c4SKumar Gala #define FDT_PCI_PREFETCH (0x40000000) 5278ab451c4SKumar Gala #define FDT_PCI_MEM32 (0x02000000) 5288ab451c4SKumar Gala #define FDT_PCI_IO (0x01000000) 5298ab451c4SKumar Gala #define FDT_PCI_MEM64 (0x03000000) 5308ab451c4SKumar Gala 5318ab451c4SKumar Gala int fdt_pci_dma_ranges(void *blob, int phb_off, struct pci_controller *hose) { 5328ab451c4SKumar Gala 5338ab451c4SKumar Gala int addrcell, sizecell, len, r; 5348ab451c4SKumar Gala u32 *dma_range; 5358ab451c4SKumar Gala /* sized based on pci addr cells, size-cells, & address-cells */ 5368ab451c4SKumar Gala u32 dma_ranges[(3 + 2 + 2) * CONFIG_SYS_PCI_NR_INBOUND_WIN]; 5378ab451c4SKumar Gala 5388ab451c4SKumar Gala addrcell = fdt_getprop_u32_default(blob, "/", "#address-cells", 1); 5398ab451c4SKumar Gala sizecell = fdt_getprop_u32_default(blob, "/", "#size-cells", 1); 5408ab451c4SKumar Gala 5418ab451c4SKumar Gala dma_range = &dma_ranges[0]; 5428ab451c4SKumar Gala for (r = 0; r < hose->region_count; r++) { 5438ab451c4SKumar Gala u64 bus_start, phys_start, size; 5448ab451c4SKumar Gala 545ff4e66e9SKumar Gala /* skip if !PCI_REGION_SYS_MEMORY */ 546ff4e66e9SKumar Gala if (!(hose->regions[r].flags & PCI_REGION_SYS_MEMORY)) 5478ab451c4SKumar Gala continue; 5488ab451c4SKumar Gala 5498ab451c4SKumar Gala bus_start = (u64)hose->regions[r].bus_start; 5508ab451c4SKumar Gala phys_start = (u64)hose->regions[r].phys_start; 5518ab451c4SKumar Gala size = (u64)hose->regions[r].size; 5528ab451c4SKumar Gala 5538ab451c4SKumar Gala dma_range[0] = 0; 554cfd700beSKumar Gala if (size >= 0x100000000ull) 5558ab451c4SKumar Gala dma_range[0] |= FDT_PCI_MEM64; 5568ab451c4SKumar Gala else 5578ab451c4SKumar Gala dma_range[0] |= FDT_PCI_MEM32; 5588ab451c4SKumar Gala if (hose->regions[r].flags & PCI_REGION_PREFETCH) 5598ab451c4SKumar Gala dma_range[0] |= FDT_PCI_PREFETCH; 5608ab451c4SKumar Gala #ifdef CONFIG_SYS_PCI_64BIT 5618ab451c4SKumar Gala dma_range[1] = bus_start >> 32; 5628ab451c4SKumar Gala #else 5638ab451c4SKumar Gala dma_range[1] = 0; 5648ab451c4SKumar Gala #endif 5658ab451c4SKumar Gala dma_range[2] = bus_start & 0xffffffff; 5668ab451c4SKumar Gala 5678ab451c4SKumar Gala if (addrcell == 2) { 5688ab451c4SKumar Gala dma_range[3] = phys_start >> 32; 5698ab451c4SKumar Gala dma_range[4] = phys_start & 0xffffffff; 5708ab451c4SKumar Gala } else { 5718ab451c4SKumar Gala dma_range[3] = phys_start & 0xffffffff; 5728ab451c4SKumar Gala } 5738ab451c4SKumar Gala 5748ab451c4SKumar Gala if (sizecell == 2) { 5758ab451c4SKumar Gala dma_range[3 + addrcell + 0] = size >> 32; 5768ab451c4SKumar Gala dma_range[3 + addrcell + 1] = size & 0xffffffff; 5778ab451c4SKumar Gala } else { 5788ab451c4SKumar Gala dma_range[3 + addrcell + 0] = size & 0xffffffff; 5798ab451c4SKumar Gala } 5808ab451c4SKumar Gala 5818ab451c4SKumar Gala dma_range += (3 + addrcell + sizecell); 5828ab451c4SKumar Gala } 5838ab451c4SKumar Gala 5848ab451c4SKumar Gala len = dma_range - &dma_ranges[0]; 5858ab451c4SKumar Gala if (len) 5868ab451c4SKumar Gala fdt_setprop(blob, phb_off, "dma-ranges", &dma_ranges[0], len*4); 5878ab451c4SKumar Gala 5888ab451c4SKumar Gala return 0; 5898ab451c4SKumar Gala } 5908ab451c4SKumar Gala #endif 59130d45c0dSStefan Roese 59230d45c0dSStefan Roese #ifdef CONFIG_FDT_FIXUP_NOR_FLASH_SIZE 59330d45c0dSStefan Roese /* 5948a805df1SStefan Roese * Provide a weak default function to return the flash bank size. 5958a805df1SStefan Roese * There might be multiple non-identical flash chips connected to one 5968a805df1SStefan Roese * chip-select, so we need to pass an index as well. 5978a805df1SStefan Roese */ 5988a805df1SStefan Roese u32 __flash_get_bank_size(int cs, int idx) 5998a805df1SStefan Roese { 6008a805df1SStefan Roese extern flash_info_t flash_info[]; 6018a805df1SStefan Roese 6028a805df1SStefan Roese /* 6038a805df1SStefan Roese * As default, a simple 1:1 mapping is provided. Boards with 6048a805df1SStefan Roese * a different mapping need to supply a board specific mapping 6058a805df1SStefan Roese * routine. 6068a805df1SStefan Roese */ 6078a805df1SStefan Roese return flash_info[cs].size; 6088a805df1SStefan Roese } 6098a805df1SStefan Roese u32 flash_get_bank_size(int cs, int idx) 6108a805df1SStefan Roese __attribute__((weak, alias("__flash_get_bank_size"))); 6118a805df1SStefan Roese 6128a805df1SStefan Roese /* 61330d45c0dSStefan Roese * This function can be used to update the size in the "reg" property 6148a805df1SStefan Roese * of all NOR FLASH device nodes. This is necessary for boards with 61530d45c0dSStefan Roese * non-fixed NOR FLASH sizes. 61630d45c0dSStefan Roese */ 6178a805df1SStefan Roese int fdt_fixup_nor_flash_size(void *blob) 61830d45c0dSStefan Roese { 61930d45c0dSStefan Roese char compat[][16] = { "cfi-flash", "jedec-flash" }; 62030d45c0dSStefan Roese int off; 62130d45c0dSStefan Roese int len; 62230d45c0dSStefan Roese struct fdt_property *prop; 6232778a014SStefan Roese u32 *reg, *reg2; 62430d45c0dSStefan Roese int i; 62530d45c0dSStefan Roese 62630d45c0dSStefan Roese for (i = 0; i < 2; i++) { 62730d45c0dSStefan Roese off = fdt_node_offset_by_compatible(blob, -1, compat[i]); 62830d45c0dSStefan Roese while (off != -FDT_ERR_NOTFOUND) { 6298a805df1SStefan Roese int idx; 6308a805df1SStefan Roese 63130d45c0dSStefan Roese /* 6328a805df1SStefan Roese * Found one compatible node, so fixup the size 6338a805df1SStefan Roese * int its reg properties 63430d45c0dSStefan Roese */ 63530d45c0dSStefan Roese prop = fdt_get_property_w(blob, off, "reg", &len); 63630d45c0dSStefan Roese if (prop) { 6378a805df1SStefan Roese int tuple_size = 3 * sizeof(reg); 63830d45c0dSStefan Roese 6398a805df1SStefan Roese /* 6408a805df1SStefan Roese * There might be multiple reg-tuples, 6418a805df1SStefan Roese * so loop through them all 6428a805df1SStefan Roese */ 6432778a014SStefan Roese reg = reg2 = (u32 *)&prop->data[0]; 6442778a014SStefan Roese for (idx = 0; idx < (len / tuple_size); idx++) { 6458a805df1SStefan Roese /* 6468a805df1SStefan Roese * Update size in reg property 6478a805df1SStefan Roese */ 6488a805df1SStefan Roese reg[2] = flash_get_bank_size(reg[0], 6498a805df1SStefan Roese idx); 6502778a014SStefan Roese 6512778a014SStefan Roese /* 6522778a014SStefan Roese * Point to next reg tuple 6532778a014SStefan Roese */ 6542778a014SStefan Roese reg += 3; 65530d45c0dSStefan Roese } 6562778a014SStefan Roese 6572778a014SStefan Roese fdt_setprop(blob, off, "reg", reg2, len); 65830d45c0dSStefan Roese } 65930d45c0dSStefan Roese 66030d45c0dSStefan Roese /* Move to next compatible node */ 66130d45c0dSStefan Roese off = fdt_node_offset_by_compatible(blob, off, 66230d45c0dSStefan Roese compat[i]); 66330d45c0dSStefan Roese } 66430d45c0dSStefan Roese } 66530d45c0dSStefan Roese 6668a805df1SStefan Roese return 0; 66730d45c0dSStefan Roese } 66830d45c0dSStefan Roese #endif 6693c950e2eSAnatolij Gustschin 6703c950e2eSAnatolij Gustschin #ifdef CONFIG_FDT_FIXUP_PARTITIONS 6713c950e2eSAnatolij Gustschin #include <jffs2/load_kernel.h> 6723c950e2eSAnatolij Gustschin #include <mtd_node.h> 6733c950e2eSAnatolij Gustschin 6743c950e2eSAnatolij Gustschin struct reg_cell { 6753c950e2eSAnatolij Gustschin unsigned int r0; 6763c950e2eSAnatolij Gustschin unsigned int r1; 6773c950e2eSAnatolij Gustschin }; 6783c950e2eSAnatolij Gustschin 6793c950e2eSAnatolij Gustschin int fdt_del_subnodes(const void *blob, int parent_offset) 6803c950e2eSAnatolij Gustschin { 6813c950e2eSAnatolij Gustschin int off, ndepth; 6823c950e2eSAnatolij Gustschin int ret; 6833c950e2eSAnatolij Gustschin 6843c950e2eSAnatolij Gustschin for (ndepth = 0, off = fdt_next_node(blob, parent_offset, &ndepth); 6853c950e2eSAnatolij Gustschin (off >= 0) && (ndepth > 0); 6863c950e2eSAnatolij Gustschin off = fdt_next_node(blob, off, &ndepth)) { 6873c950e2eSAnatolij Gustschin if (ndepth == 1) { 6883c950e2eSAnatolij Gustschin debug("delete %s: offset: %x\n", 6893c950e2eSAnatolij Gustschin fdt_get_name(blob, off, 0), off); 6903c950e2eSAnatolij Gustschin ret = fdt_del_node((void *)blob, off); 6913c950e2eSAnatolij Gustschin if (ret < 0) { 6923c950e2eSAnatolij Gustschin printf("Can't delete node: %s\n", 6933c950e2eSAnatolij Gustschin fdt_strerror(ret)); 6943c950e2eSAnatolij Gustschin return ret; 6953c950e2eSAnatolij Gustschin } else { 6963c950e2eSAnatolij Gustschin ndepth = 0; 6973c950e2eSAnatolij Gustschin off = parent_offset; 6983c950e2eSAnatolij Gustschin } 6993c950e2eSAnatolij Gustschin } 7003c950e2eSAnatolij Gustschin } 7013c950e2eSAnatolij Gustschin return 0; 7023c950e2eSAnatolij Gustschin } 7033c950e2eSAnatolij Gustschin 7043c950e2eSAnatolij Gustschin int fdt_increase_size(void *fdt, int add_len) 7053c950e2eSAnatolij Gustschin { 7063c950e2eSAnatolij Gustschin int newlen; 7073c950e2eSAnatolij Gustschin 7083c950e2eSAnatolij Gustschin newlen = fdt_totalsize(fdt) + add_len; 7093c950e2eSAnatolij Gustschin 7103c950e2eSAnatolij Gustschin /* Open in place with a new len */ 7113c950e2eSAnatolij Gustschin return fdt_open_into(fdt, fdt, newlen); 7123c950e2eSAnatolij Gustschin } 7133c950e2eSAnatolij Gustschin 7143c950e2eSAnatolij Gustschin int fdt_del_partitions(void *blob, int parent_offset) 7153c950e2eSAnatolij Gustschin { 7163c950e2eSAnatolij Gustschin const void *prop; 7173c950e2eSAnatolij Gustschin int ndepth = 0; 7183c950e2eSAnatolij Gustschin int off; 7193c950e2eSAnatolij Gustschin int ret; 7203c950e2eSAnatolij Gustschin 7213c950e2eSAnatolij Gustschin off = fdt_next_node(blob, parent_offset, &ndepth); 7223c950e2eSAnatolij Gustschin if (off > 0 && ndepth == 1) { 7233c950e2eSAnatolij Gustschin prop = fdt_getprop(blob, off, "label", NULL); 7243c950e2eSAnatolij Gustschin if (prop == NULL) { 7253c950e2eSAnatolij Gustschin /* 7263c950e2eSAnatolij Gustschin * Could not find label property, nand {}; node? 7273c950e2eSAnatolij Gustschin * Check subnode, delete partitions there if any. 7283c950e2eSAnatolij Gustschin */ 7293c950e2eSAnatolij Gustschin return fdt_del_partitions(blob, off); 7303c950e2eSAnatolij Gustschin } else { 7313c950e2eSAnatolij Gustschin ret = fdt_del_subnodes(blob, parent_offset); 7323c950e2eSAnatolij Gustschin if (ret < 0) { 7333c950e2eSAnatolij Gustschin printf("Can't remove subnodes: %s\n", 7343c950e2eSAnatolij Gustschin fdt_strerror(ret)); 7353c950e2eSAnatolij Gustschin return ret; 7363c950e2eSAnatolij Gustschin } 7373c950e2eSAnatolij Gustschin } 7383c950e2eSAnatolij Gustschin } 7393c950e2eSAnatolij Gustschin return 0; 7403c950e2eSAnatolij Gustschin } 7413c950e2eSAnatolij Gustschin 7423c950e2eSAnatolij Gustschin int fdt_node_set_part_info(void *blob, int parent_offset, 7433c950e2eSAnatolij Gustschin struct mtd_device *dev) 7443c950e2eSAnatolij Gustschin { 7453c950e2eSAnatolij Gustschin struct list_head *pentry; 7463c950e2eSAnatolij Gustschin struct part_info *part; 7473c950e2eSAnatolij Gustschin struct reg_cell cell; 7483c950e2eSAnatolij Gustschin int off, ndepth = 0; 7493c950e2eSAnatolij Gustschin int part_num, ret; 7503c950e2eSAnatolij Gustschin char buf[64]; 7513c950e2eSAnatolij Gustschin 7523c950e2eSAnatolij Gustschin ret = fdt_del_partitions(blob, parent_offset); 7533c950e2eSAnatolij Gustschin if (ret < 0) 7543c950e2eSAnatolij Gustschin return ret; 7553c950e2eSAnatolij Gustschin 7563c950e2eSAnatolij Gustschin /* 7573c950e2eSAnatolij Gustschin * Check if it is nand {}; subnode, adjust 7583c950e2eSAnatolij Gustschin * the offset in this case 7593c950e2eSAnatolij Gustschin */ 7603c950e2eSAnatolij Gustschin off = fdt_next_node(blob, parent_offset, &ndepth); 7613c950e2eSAnatolij Gustschin if (off > 0 && ndepth == 1) 7623c950e2eSAnatolij Gustschin parent_offset = off; 7633c950e2eSAnatolij Gustschin 7643c950e2eSAnatolij Gustschin part_num = 0; 7653c950e2eSAnatolij Gustschin list_for_each_prev(pentry, &dev->parts) { 7663c950e2eSAnatolij Gustschin int newoff; 7673c950e2eSAnatolij Gustschin 7683c950e2eSAnatolij Gustschin part = list_entry(pentry, struct part_info, link); 7693c950e2eSAnatolij Gustschin 7703c950e2eSAnatolij Gustschin debug("%2d: %-20s0x%08x\t0x%08x\t%d\n", 7713c950e2eSAnatolij Gustschin part_num, part->name, part->size, 7723c950e2eSAnatolij Gustschin part->offset, part->mask_flags); 7733c950e2eSAnatolij Gustschin 7743c950e2eSAnatolij Gustschin sprintf(buf, "partition@%x", part->offset); 7753c950e2eSAnatolij Gustschin add_sub: 7763c950e2eSAnatolij Gustschin ret = fdt_add_subnode(blob, parent_offset, buf); 7773c950e2eSAnatolij Gustschin if (ret == -FDT_ERR_NOSPACE) { 7783c950e2eSAnatolij Gustschin ret = fdt_increase_size(blob, 512); 7793c950e2eSAnatolij Gustschin if (!ret) 7803c950e2eSAnatolij Gustschin goto add_sub; 7813c950e2eSAnatolij Gustschin else 7823c950e2eSAnatolij Gustschin goto err_size; 7833c950e2eSAnatolij Gustschin } else if (ret < 0) { 7843c950e2eSAnatolij Gustschin printf("Can't add partition node: %s\n", 7853c950e2eSAnatolij Gustschin fdt_strerror(ret)); 7863c950e2eSAnatolij Gustschin return ret; 7873c950e2eSAnatolij Gustschin } 7883c950e2eSAnatolij Gustschin newoff = ret; 7893c950e2eSAnatolij Gustschin 7903c950e2eSAnatolij Gustschin /* Check MTD_WRITEABLE_CMD flag */ 7913c950e2eSAnatolij Gustschin if (part->mask_flags & 1) { 7923c950e2eSAnatolij Gustschin add_ro: 7933c950e2eSAnatolij Gustschin ret = fdt_setprop(blob, newoff, "read_only", NULL, 0); 7943c950e2eSAnatolij Gustschin if (ret == -FDT_ERR_NOSPACE) { 7953c950e2eSAnatolij Gustschin ret = fdt_increase_size(blob, 512); 7963c950e2eSAnatolij Gustschin if (!ret) 7973c950e2eSAnatolij Gustschin goto add_ro; 7983c950e2eSAnatolij Gustschin else 7993c950e2eSAnatolij Gustschin goto err_size; 8003c950e2eSAnatolij Gustschin } else if (ret < 0) 8013c950e2eSAnatolij Gustschin goto err_prop; 8023c950e2eSAnatolij Gustschin } 8033c950e2eSAnatolij Gustschin 8043c950e2eSAnatolij Gustschin cell.r0 = cpu_to_fdt32(part->offset); 8053c950e2eSAnatolij Gustschin cell.r1 = cpu_to_fdt32(part->size); 8063c950e2eSAnatolij Gustschin add_reg: 8073c950e2eSAnatolij Gustschin ret = fdt_setprop(blob, newoff, "reg", &cell, sizeof(cell)); 8083c950e2eSAnatolij Gustschin if (ret == -FDT_ERR_NOSPACE) { 8093c950e2eSAnatolij Gustschin ret = fdt_increase_size(blob, 512); 8103c950e2eSAnatolij Gustschin if (!ret) 8113c950e2eSAnatolij Gustschin goto add_reg; 8123c950e2eSAnatolij Gustschin else 8133c950e2eSAnatolij Gustschin goto err_size; 8143c950e2eSAnatolij Gustschin } else if (ret < 0) 8153c950e2eSAnatolij Gustschin goto err_prop; 8163c950e2eSAnatolij Gustschin 8173c950e2eSAnatolij Gustschin add_label: 8183c950e2eSAnatolij Gustschin ret = fdt_setprop_string(blob, newoff, "label", part->name); 8193c950e2eSAnatolij Gustschin if (ret == -FDT_ERR_NOSPACE) { 8203c950e2eSAnatolij Gustschin ret = fdt_increase_size(blob, 512); 8213c950e2eSAnatolij Gustschin if (!ret) 8223c950e2eSAnatolij Gustschin goto add_label; 8233c950e2eSAnatolij Gustschin else 8243c950e2eSAnatolij Gustschin goto err_size; 8253c950e2eSAnatolij Gustschin } else if (ret < 0) 8263c950e2eSAnatolij Gustschin goto err_prop; 8273c950e2eSAnatolij Gustschin 8283c950e2eSAnatolij Gustschin part_num++; 8293c950e2eSAnatolij Gustschin } 8303c950e2eSAnatolij Gustschin return 0; 8313c950e2eSAnatolij Gustschin err_size: 8323c950e2eSAnatolij Gustschin printf("Can't increase blob size: %s\n", fdt_strerror(ret)); 8333c950e2eSAnatolij Gustschin return ret; 8343c950e2eSAnatolij Gustschin err_prop: 8353c950e2eSAnatolij Gustschin printf("Can't add property: %s\n", fdt_strerror(ret)); 8363c950e2eSAnatolij Gustschin return ret; 8373c950e2eSAnatolij Gustschin } 8383c950e2eSAnatolij Gustschin 8393c950e2eSAnatolij Gustschin /* 8403c950e2eSAnatolij Gustschin * Update partitions in nor/nand nodes using info from 8413c950e2eSAnatolij Gustschin * mtdparts environment variable. The nodes to update are 8423c950e2eSAnatolij Gustschin * specified by node_info structure which contains mtd device 8433c950e2eSAnatolij Gustschin * type and compatible string: E. g. the board code in 8443c950e2eSAnatolij Gustschin * ft_board_setup() could use: 8453c950e2eSAnatolij Gustschin * 8463c950e2eSAnatolij Gustschin * struct node_info nodes[] = { 8473c950e2eSAnatolij Gustschin * { "fsl,mpc5121-nfc", MTD_DEV_TYPE_NAND, }, 8483c950e2eSAnatolij Gustschin * { "cfi-flash", MTD_DEV_TYPE_NOR, }, 8493c950e2eSAnatolij Gustschin * }; 8503c950e2eSAnatolij Gustschin * 8513c950e2eSAnatolij Gustschin * fdt_fixup_mtdparts(blob, nodes, ARRAY_SIZE(nodes)); 8523c950e2eSAnatolij Gustschin */ 8533c950e2eSAnatolij Gustschin void fdt_fixup_mtdparts(void *blob, void *node_info, int node_info_size) 8543c950e2eSAnatolij Gustschin { 8553c950e2eSAnatolij Gustschin struct node_info *ni = node_info; 8563c950e2eSAnatolij Gustschin struct mtd_device *dev; 8573c950e2eSAnatolij Gustschin char *parts; 8583c950e2eSAnatolij Gustschin int i, idx; 8593c950e2eSAnatolij Gustschin int noff; 8603c950e2eSAnatolij Gustschin 8613c950e2eSAnatolij Gustschin parts = getenv("mtdparts"); 8623c950e2eSAnatolij Gustschin if (!parts) 8633c950e2eSAnatolij Gustschin return; 8643c950e2eSAnatolij Gustschin 8653c950e2eSAnatolij Gustschin if (mtdparts_init() != 0) 8663c950e2eSAnatolij Gustschin return; 8673c950e2eSAnatolij Gustschin 8683c950e2eSAnatolij Gustschin for (i = 0; i < node_info_size; i++) { 8693c950e2eSAnatolij Gustschin idx = 0; 8703c950e2eSAnatolij Gustschin noff = fdt_node_offset_by_compatible(blob, -1, ni[i].compat); 8713c950e2eSAnatolij Gustschin while (noff != -FDT_ERR_NOTFOUND) { 8723c950e2eSAnatolij Gustschin debug("%s: %s, mtd dev type %d\n", 8733c950e2eSAnatolij Gustschin fdt_get_name(blob, noff, 0), 8743c950e2eSAnatolij Gustschin ni[i].compat, ni[i].type); 8753c950e2eSAnatolij Gustschin dev = device_find(ni[i].type, idx++); 8763c950e2eSAnatolij Gustschin if (dev) { 8773c950e2eSAnatolij Gustschin if (fdt_node_set_part_info(blob, noff, dev)) 8783c950e2eSAnatolij Gustschin return; /* return on error */ 8793c950e2eSAnatolij Gustschin } 8803c950e2eSAnatolij Gustschin 8813c950e2eSAnatolij Gustschin /* Jump to next flash node */ 8823c950e2eSAnatolij Gustschin noff = fdt_node_offset_by_compatible(blob, noff, 8833c950e2eSAnatolij Gustschin ni[i].compat); 8843c950e2eSAnatolij Gustschin } 8853c950e2eSAnatolij Gustschin } 8863c950e2eSAnatolij Gustschin } 8873c950e2eSAnatolij Gustschin #endif 88849b97d9cSKumar Gala 88949b97d9cSKumar Gala void fdt_del_node_and_alias(void *blob, const char *alias) 89049b97d9cSKumar Gala { 89149b97d9cSKumar Gala int off = fdt_path_offset(blob, alias); 89249b97d9cSKumar Gala 89349b97d9cSKumar Gala if (off < 0) 89449b97d9cSKumar Gala return; 89549b97d9cSKumar Gala 89649b97d9cSKumar Gala fdt_del_node(blob, off); 89749b97d9cSKumar Gala 89849b97d9cSKumar Gala off = fdt_path_offset(blob, "/aliases"); 89949b97d9cSKumar Gala fdt_delprop(blob, off, alias); 90049b97d9cSKumar Gala } 901a0342c08SKumar Gala 902a0342c08SKumar Gala /* Helper to read a big number; size is in cells (not bytes) */ 903a0342c08SKumar Gala static inline u64 of_read_number(const __be32 *cell, int size) 904a0342c08SKumar Gala { 905a0342c08SKumar Gala u64 r = 0; 906a0342c08SKumar Gala while (size--) 907a0342c08SKumar Gala r = (r << 32) | be32_to_cpu(*(cell++)); 908a0342c08SKumar Gala return r; 909a0342c08SKumar Gala } 910a0342c08SKumar Gala 911a0342c08SKumar Gala #define PRu64 "%llx" 912a0342c08SKumar Gala 913a0342c08SKumar Gala /* Max address size we deal with */ 914a0342c08SKumar Gala #define OF_MAX_ADDR_CELLS 4 915a0342c08SKumar Gala #define OF_BAD_ADDR ((u64)-1) 916a0342c08SKumar Gala #define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \ 917a0342c08SKumar Gala (ns) > 0) 918a0342c08SKumar Gala 919a0342c08SKumar Gala /* Debug utility */ 920a0342c08SKumar Gala #ifdef DEBUG 921a0342c08SKumar Gala static void of_dump_addr(const char *s, const u32 *addr, int na) 922a0342c08SKumar Gala { 923a0342c08SKumar Gala printf("%s", s); 924a0342c08SKumar Gala while(na--) 925a0342c08SKumar Gala printf(" %08x", *(addr++)); 926a0342c08SKumar Gala printf("\n"); 927a0342c08SKumar Gala } 928a0342c08SKumar Gala #else 929a0342c08SKumar Gala static void of_dump_addr(const char *s, const u32 *addr, int na) { } 930a0342c08SKumar Gala #endif 931a0342c08SKumar Gala 932a0342c08SKumar Gala /* Callbacks for bus specific translators */ 933a0342c08SKumar Gala struct of_bus { 934a0342c08SKumar Gala const char *name; 935a0342c08SKumar Gala const char *addresses; 9366395f318SScott Wood void (*count_cells)(void *blob, int parentoffset, 937a0342c08SKumar Gala int *addrc, int *sizec); 938a0342c08SKumar Gala u64 (*map)(u32 *addr, const u32 *range, 939a0342c08SKumar Gala int na, int ns, int pna); 940a0342c08SKumar Gala int (*translate)(u32 *addr, u64 offset, int na); 941a0342c08SKumar Gala }; 942a0342c08SKumar Gala 943a0342c08SKumar Gala /* Default translator (generic bus) */ 9446395f318SScott Wood static void of_bus_default_count_cells(void *blob, int parentoffset, 945a0342c08SKumar Gala int *addrc, int *sizec) 946a0342c08SKumar Gala { 9476395f318SScott Wood const u32 *prop; 9486395f318SScott Wood 9496395f318SScott Wood if (addrc) { 9506395f318SScott Wood prop = fdt_getprop(blob, parentoffset, "#address-cells", NULL); 9516395f318SScott Wood if (prop) 9526395f318SScott Wood *addrc = be32_to_cpup(prop); 9536395f318SScott Wood else 9546395f318SScott Wood *addrc = 2; 9556395f318SScott Wood } 9566395f318SScott Wood 9576395f318SScott Wood if (sizec) { 9586395f318SScott Wood prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL); 9596395f318SScott Wood if (prop) 9606395f318SScott Wood *sizec = be32_to_cpup(prop); 9616395f318SScott Wood else 9626395f318SScott Wood *sizec = 1; 9636395f318SScott Wood } 964a0342c08SKumar Gala } 965a0342c08SKumar Gala 966a0342c08SKumar Gala static u64 of_bus_default_map(u32 *addr, const u32 *range, 967a0342c08SKumar Gala int na, int ns, int pna) 968a0342c08SKumar Gala { 969a0342c08SKumar Gala u64 cp, s, da; 970a0342c08SKumar Gala 971a0342c08SKumar Gala cp = of_read_number(range, na); 972a0342c08SKumar Gala s = of_read_number(range + na + pna, ns); 973a0342c08SKumar Gala da = of_read_number(addr, na); 974a0342c08SKumar Gala 975a0342c08SKumar Gala debug("OF: default map, cp="PRu64", s="PRu64", da="PRu64"\n", 976a0342c08SKumar Gala cp, s, da); 977a0342c08SKumar Gala 978a0342c08SKumar Gala if (da < cp || da >= (cp + s)) 979a0342c08SKumar Gala return OF_BAD_ADDR; 980a0342c08SKumar Gala return da - cp; 981a0342c08SKumar Gala } 982a0342c08SKumar Gala 983a0342c08SKumar Gala static int of_bus_default_translate(u32 *addr, u64 offset, int na) 984a0342c08SKumar Gala { 985a0342c08SKumar Gala u64 a = of_read_number(addr, na); 986a0342c08SKumar Gala memset(addr, 0, na * 4); 987a0342c08SKumar Gala a += offset; 988a0342c08SKumar Gala if (na > 1) 989a0342c08SKumar Gala addr[na - 2] = a >> 32; 990a0342c08SKumar Gala addr[na - 1] = a & 0xffffffffu; 991a0342c08SKumar Gala 992a0342c08SKumar Gala return 0; 993a0342c08SKumar Gala } 994a0342c08SKumar Gala 995a0342c08SKumar Gala /* Array of bus specific translators */ 996a0342c08SKumar Gala static struct of_bus of_busses[] = { 997a0342c08SKumar Gala /* Default */ 998a0342c08SKumar Gala { 999a0342c08SKumar Gala .name = "default", 1000a0342c08SKumar Gala .addresses = "reg", 1001a0342c08SKumar Gala .count_cells = of_bus_default_count_cells, 1002a0342c08SKumar Gala .map = of_bus_default_map, 1003a0342c08SKumar Gala .translate = of_bus_default_translate, 1004a0342c08SKumar Gala }, 1005a0342c08SKumar Gala }; 1006a0342c08SKumar Gala 1007a0342c08SKumar Gala static int of_translate_one(void * blob, int parent, struct of_bus *bus, 1008a0342c08SKumar Gala struct of_bus *pbus, u32 *addr, 1009a0342c08SKumar Gala int na, int ns, int pna, const char *rprop) 1010a0342c08SKumar Gala { 1011a0342c08SKumar Gala const u32 *ranges; 1012a0342c08SKumar Gala int rlen; 1013a0342c08SKumar Gala int rone; 1014a0342c08SKumar Gala u64 offset = OF_BAD_ADDR; 1015a0342c08SKumar Gala 1016a0342c08SKumar Gala /* Normally, an absence of a "ranges" property means we are 1017a0342c08SKumar Gala * crossing a non-translatable boundary, and thus the addresses 1018a0342c08SKumar Gala * below the current not cannot be converted to CPU physical ones. 1019a0342c08SKumar Gala * Unfortunately, while this is very clear in the spec, it's not 1020a0342c08SKumar Gala * what Apple understood, and they do have things like /uni-n or 1021a0342c08SKumar Gala * /ht nodes with no "ranges" property and a lot of perfectly 1022a0342c08SKumar Gala * useable mapped devices below them. Thus we treat the absence of 1023a0342c08SKumar Gala * "ranges" as equivalent to an empty "ranges" property which means 1024a0342c08SKumar Gala * a 1:1 translation at that level. It's up to the caller not to try 1025a0342c08SKumar Gala * to translate addresses that aren't supposed to be translated in 1026a0342c08SKumar Gala * the first place. --BenH. 1027a0342c08SKumar Gala */ 1028a0342c08SKumar Gala ranges = (u32 *)fdt_getprop(blob, parent, rprop, &rlen); 1029a0342c08SKumar Gala if (ranges == NULL || rlen == 0) { 1030a0342c08SKumar Gala offset = of_read_number(addr, na); 1031a0342c08SKumar Gala memset(addr, 0, pna * 4); 1032a0342c08SKumar Gala debug("OF: no ranges, 1:1 translation\n"); 1033a0342c08SKumar Gala goto finish; 1034a0342c08SKumar Gala } 1035a0342c08SKumar Gala 1036a0342c08SKumar Gala debug("OF: walking ranges...\n"); 1037a0342c08SKumar Gala 1038a0342c08SKumar Gala /* Now walk through the ranges */ 1039a0342c08SKumar Gala rlen /= 4; 1040a0342c08SKumar Gala rone = na + pna + ns; 1041a0342c08SKumar Gala for (; rlen >= rone; rlen -= rone, ranges += rone) { 1042a0342c08SKumar Gala offset = bus->map(addr, ranges, na, ns, pna); 1043a0342c08SKumar Gala if (offset != OF_BAD_ADDR) 1044a0342c08SKumar Gala break; 1045a0342c08SKumar Gala } 1046a0342c08SKumar Gala if (offset == OF_BAD_ADDR) { 1047a0342c08SKumar Gala debug("OF: not found !\n"); 1048a0342c08SKumar Gala return 1; 1049a0342c08SKumar Gala } 1050a0342c08SKumar Gala memcpy(addr, ranges + na, 4 * pna); 1051a0342c08SKumar Gala 1052a0342c08SKumar Gala finish: 1053a0342c08SKumar Gala of_dump_addr("OF: parent translation for:", addr, pna); 1054a0342c08SKumar Gala debug("OF: with offset: "PRu64"\n", offset); 1055a0342c08SKumar Gala 1056a0342c08SKumar Gala /* Translate it into parent bus space */ 1057a0342c08SKumar Gala return pbus->translate(addr, offset, pna); 1058a0342c08SKumar Gala } 1059a0342c08SKumar Gala 1060a0342c08SKumar Gala /* 1061a0342c08SKumar Gala * Translate an address from the device-tree into a CPU physical address, 1062a0342c08SKumar Gala * this walks up the tree and applies the various bus mappings on the 1063a0342c08SKumar Gala * way. 1064a0342c08SKumar Gala * 1065a0342c08SKumar Gala * Note: We consider that crossing any level with #size-cells == 0 to mean 1066a0342c08SKumar Gala * that translation is impossible (that is we are not dealing with a value 1067a0342c08SKumar Gala * that can be mapped to a cpu physical address). This is not really specified 1068a0342c08SKumar Gala * that way, but this is traditionally the way IBM at least do things 1069a0342c08SKumar Gala */ 1070a0342c08SKumar Gala u64 __of_translate_address(void *blob, int node_offset, const u32 *in_addr, 1071a0342c08SKumar Gala const char *rprop) 1072a0342c08SKumar Gala { 1073a0342c08SKumar Gala int parent; 1074a0342c08SKumar Gala struct of_bus *bus, *pbus; 1075a0342c08SKumar Gala u32 addr[OF_MAX_ADDR_CELLS]; 1076a0342c08SKumar Gala int na, ns, pna, pns; 1077a0342c08SKumar Gala u64 result = OF_BAD_ADDR; 1078a0342c08SKumar Gala 1079a0342c08SKumar Gala debug("OF: ** translation for device %s **\n", 1080a0342c08SKumar Gala fdt_get_name(blob, node_offset, NULL)); 1081a0342c08SKumar Gala 1082a0342c08SKumar Gala /* Get parent & match bus type */ 1083a0342c08SKumar Gala parent = fdt_parent_offset(blob, node_offset); 1084a0342c08SKumar Gala if (parent < 0) 1085a0342c08SKumar Gala goto bail; 1086a0342c08SKumar Gala bus = &of_busses[0]; 1087a0342c08SKumar Gala 1088a0342c08SKumar Gala /* Cound address cells & copy address locally */ 10896395f318SScott Wood bus->count_cells(blob, parent, &na, &ns); 1090a0342c08SKumar Gala if (!OF_CHECK_COUNTS(na, ns)) { 1091a0342c08SKumar Gala printf("%s: Bad cell count for %s\n", __FUNCTION__, 1092a0342c08SKumar Gala fdt_get_name(blob, node_offset, NULL)); 1093a0342c08SKumar Gala goto bail; 1094a0342c08SKumar Gala } 1095a0342c08SKumar Gala memcpy(addr, in_addr, na * 4); 1096a0342c08SKumar Gala 1097a0342c08SKumar Gala debug("OF: bus is %s (na=%d, ns=%d) on %s\n", 1098a0342c08SKumar Gala bus->name, na, ns, fdt_get_name(blob, parent, NULL)); 1099a0342c08SKumar Gala of_dump_addr("OF: translating address:", addr, na); 1100a0342c08SKumar Gala 1101a0342c08SKumar Gala /* Translate */ 1102a0342c08SKumar Gala for (;;) { 1103a0342c08SKumar Gala /* Switch to parent bus */ 1104a0342c08SKumar Gala node_offset = parent; 1105a0342c08SKumar Gala parent = fdt_parent_offset(blob, node_offset); 1106a0342c08SKumar Gala 1107a0342c08SKumar Gala /* If root, we have finished */ 1108a0342c08SKumar Gala if (parent < 0) { 1109a0342c08SKumar Gala debug("OF: reached root node\n"); 1110a0342c08SKumar Gala result = of_read_number(addr, na); 1111a0342c08SKumar Gala break; 1112a0342c08SKumar Gala } 1113a0342c08SKumar Gala 1114a0342c08SKumar Gala /* Get new parent bus and counts */ 1115a0342c08SKumar Gala pbus = &of_busses[0]; 11166395f318SScott Wood pbus->count_cells(blob, parent, &pna, &pns); 1117a0342c08SKumar Gala if (!OF_CHECK_COUNTS(pna, pns)) { 1118a0342c08SKumar Gala printf("%s: Bad cell count for %s\n", __FUNCTION__, 1119a0342c08SKumar Gala fdt_get_name(blob, node_offset, NULL)); 1120a0342c08SKumar Gala break; 1121a0342c08SKumar Gala } 1122a0342c08SKumar Gala 1123a0342c08SKumar Gala debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n", 1124a0342c08SKumar Gala pbus->name, pna, pns, fdt_get_name(blob, parent, NULL)); 1125a0342c08SKumar Gala 1126a0342c08SKumar Gala /* Apply bus translation */ 1127a0342c08SKumar Gala if (of_translate_one(blob, node_offset, bus, pbus, 1128a0342c08SKumar Gala addr, na, ns, pna, rprop)) 1129a0342c08SKumar Gala break; 1130a0342c08SKumar Gala 1131a0342c08SKumar Gala /* Complete the move up one level */ 1132a0342c08SKumar Gala na = pna; 1133a0342c08SKumar Gala ns = pns; 1134a0342c08SKumar Gala bus = pbus; 1135a0342c08SKumar Gala 1136a0342c08SKumar Gala of_dump_addr("OF: one level translation:", addr, na); 1137a0342c08SKumar Gala } 1138a0342c08SKumar Gala bail: 1139a0342c08SKumar Gala 1140a0342c08SKumar Gala return result; 1141a0342c08SKumar Gala } 1142a0342c08SKumar Gala 1143a0342c08SKumar Gala u64 fdt_translate_address(void *blob, int node_offset, const u32 *in_addr) 1144a0342c08SKumar Gala { 1145a0342c08SKumar Gala return __of_translate_address(blob, node_offset, in_addr, "ranges"); 1146a0342c08SKumar Gala } 114775e73afdSKumar Gala 114875e73afdSKumar Gala /** 114975e73afdSKumar Gala * fdt_node_offset_by_compat_reg: Find a node that matches compatiable and 115075e73afdSKumar Gala * who's reg property matches a physical cpu address 115175e73afdSKumar Gala * 115275e73afdSKumar Gala * @blob: ptr to device tree 115375e73afdSKumar Gala * @compat: compatiable string to match 115475e73afdSKumar Gala * @compat_off: property name 115575e73afdSKumar Gala * 115675e73afdSKumar Gala */ 115775e73afdSKumar Gala int fdt_node_offset_by_compat_reg(void *blob, const char *compat, 115875e73afdSKumar Gala phys_addr_t compat_off) 115975e73afdSKumar Gala { 116075e73afdSKumar Gala int len, off = fdt_node_offset_by_compatible(blob, -1, compat); 116175e73afdSKumar Gala while (off != -FDT_ERR_NOTFOUND) { 116275e73afdSKumar Gala u32 *reg = (u32 *)fdt_getprop(blob, off, "reg", &len); 116375e73afdSKumar Gala if (reg) { 116475e73afdSKumar Gala if (compat_off == fdt_translate_address(blob, off, reg)) 116575e73afdSKumar Gala return off; 116675e73afdSKumar Gala } 116775e73afdSKumar Gala off = fdt_node_offset_by_compatible(blob, off, compat); 116875e73afdSKumar Gala } 116975e73afdSKumar Gala 117075e73afdSKumar Gala return -FDT_ERR_NOTFOUND; 117175e73afdSKumar Gala } 117275e73afdSKumar Gala 1173b4b847e9SKumar Gala /** 1174b4b847e9SKumar Gala * fdt_alloc_phandle: Return next free phandle value 1175b4b847e9SKumar Gala * 1176b4b847e9SKumar Gala * @blob: ptr to device tree 1177b4b847e9SKumar Gala */ 1178b4b847e9SKumar Gala int fdt_alloc_phandle(void *blob) 1179b4b847e9SKumar Gala { 1180b4b847e9SKumar Gala int offset, len, phandle = 0; 1181b4b847e9SKumar Gala const u32 *val; 118275e73afdSKumar Gala 1183b4b847e9SKumar Gala for (offset = fdt_next_node(blob, -1, NULL); offset >= 0; 1184b4b847e9SKumar Gala offset = fdt_next_node(blob, offset, NULL)) { 1185b4b847e9SKumar Gala val = fdt_getprop(blob, offset, "linux,phandle", &len); 1186b4b847e9SKumar Gala if (val) 1187b4b847e9SKumar Gala phandle = max(*val, phandle); 1188b4b847e9SKumar Gala } 1189b4b847e9SKumar Gala 1190b4b847e9SKumar Gala return phandle + 1; 1191b4b847e9SKumar Gala } 1192*beca5a5fSAnatolij Gustschin 1193*beca5a5fSAnatolij Gustschin #if defined(CONFIG_VIDEO) 1194*beca5a5fSAnatolij Gustschin int fdt_add_edid(void *blob, const char *compat, unsigned char *edid_buf) 1195*beca5a5fSAnatolij Gustschin { 1196*beca5a5fSAnatolij Gustschin int noff; 1197*beca5a5fSAnatolij Gustschin int ret; 1198*beca5a5fSAnatolij Gustschin 1199*beca5a5fSAnatolij Gustschin noff = fdt_node_offset_by_compatible(blob, -1, compat); 1200*beca5a5fSAnatolij Gustschin if (noff != -FDT_ERR_NOTFOUND) { 1201*beca5a5fSAnatolij Gustschin debug("%s: %s\n", fdt_get_name(blob, noff, 0), compat); 1202*beca5a5fSAnatolij Gustschin add_edid: 1203*beca5a5fSAnatolij Gustschin ret = fdt_setprop(blob, noff, "edid", edid_buf, 128); 1204*beca5a5fSAnatolij Gustschin if (ret == -FDT_ERR_NOSPACE) { 1205*beca5a5fSAnatolij Gustschin ret = fdt_increase_size(blob, 512); 1206*beca5a5fSAnatolij Gustschin if (!ret) 1207*beca5a5fSAnatolij Gustschin goto add_edid; 1208*beca5a5fSAnatolij Gustschin else 1209*beca5a5fSAnatolij Gustschin goto err_size; 1210*beca5a5fSAnatolij Gustschin } else if (ret < 0) { 1211*beca5a5fSAnatolij Gustschin printf("Can't add property: %s\n", fdt_strerror(ret)); 1212*beca5a5fSAnatolij Gustschin return ret; 1213*beca5a5fSAnatolij Gustschin } 1214*beca5a5fSAnatolij Gustschin } 1215*beca5a5fSAnatolij Gustschin return 0; 1216*beca5a5fSAnatolij Gustschin err_size: 1217*beca5a5fSAnatolij Gustschin printf("Can't increase blob size: %s\n", fdt_strerror(ret)); 1218*beca5a5fSAnatolij Gustschin return ret; 1219*beca5a5fSAnatolij Gustschin } 1220*beca5a5fSAnatolij Gustschin #endif 1221