183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+ 264dbbd40SGerald Van Baren /* 364dbbd40SGerald Van Baren * (C) Copyright 2007 464dbbd40SGerald Van Baren * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com 564dbbd40SGerald Van Baren * 62a523f52SShengzhou Liu * Copyright 2010-2011 Freescale Semiconductor, Inc. 764dbbd40SGerald Van Baren */ 864dbbd40SGerald Van Baren 964dbbd40SGerald Van Baren #include <common.h> 10e48f3741SSimon Glass #include <inttypes.h> 113e303f74SAnton Vorontsov #include <stdio_dev.h> 1264dbbd40SGerald Van Baren #include <linux/ctype.h> 1364dbbd40SGerald Van Baren #include <linux/types.h> 1464dbbd40SGerald Van Baren #include <asm/global_data.h> 15b08c8c48SMasahiro Yamada #include <linux/libfdt.h> 1664dbbd40SGerald Van Baren #include <fdt_support.h> 17151c8b09SKumar Gala #include <exports.h> 18a0ae380bSBin Meng #include <fdtdec.h> 1964dbbd40SGerald Van Baren 203bed2aafSKumar Gala /** 2194fb182cSAlexander Graf * fdt_getprop_u32_default_node - Return a node's property or a default 2294fb182cSAlexander Graf * 2394fb182cSAlexander Graf * @fdt: ptr to device tree 2494fb182cSAlexander Graf * @off: offset of node 2594fb182cSAlexander Graf * @cell: cell offset in property 2694fb182cSAlexander Graf * @prop: property name 2794fb182cSAlexander Graf * @dflt: default value if the property isn't found 2894fb182cSAlexander Graf * 2994fb182cSAlexander Graf * Convenience function to return a node's property or a default value if 3094fb182cSAlexander Graf * the property doesn't exist. 3194fb182cSAlexander Graf */ 3294fb182cSAlexander Graf u32 fdt_getprop_u32_default_node(const void *fdt, int off, int cell, 3394fb182cSAlexander Graf const char *prop, const u32 dflt) 3494fb182cSAlexander Graf { 3594fb182cSAlexander Graf const fdt32_t *val; 3694fb182cSAlexander Graf int len; 3794fb182cSAlexander Graf 3894fb182cSAlexander Graf val = fdt_getprop(fdt, off, prop, &len); 3994fb182cSAlexander Graf 4094fb182cSAlexander Graf /* Check if property exists */ 4194fb182cSAlexander Graf if (!val) 4294fb182cSAlexander Graf return dflt; 4394fb182cSAlexander Graf 4494fb182cSAlexander Graf /* Check if property is long enough */ 4594fb182cSAlexander Graf if (len < ((cell + 1) * sizeof(uint32_t))) 4694fb182cSAlexander Graf return dflt; 4794fb182cSAlexander Graf 4894fb182cSAlexander Graf return fdt32_to_cpu(*val); 4994fb182cSAlexander Graf } 5094fb182cSAlexander Graf 5194fb182cSAlexander Graf /** 523bed2aafSKumar Gala * fdt_getprop_u32_default - Find a node and return it's property or a default 533bed2aafSKumar Gala * 543bed2aafSKumar Gala * @fdt: ptr to device tree 553bed2aafSKumar Gala * @path: path of node 563bed2aafSKumar Gala * @prop: property name 573bed2aafSKumar Gala * @dflt: default value if the property isn't found 583bed2aafSKumar Gala * 593bed2aafSKumar Gala * Convenience function to find a node and return it's property or a 603bed2aafSKumar Gala * default value if it doesn't exist. 613bed2aafSKumar Gala */ 6207e12784SGabe Black u32 fdt_getprop_u32_default(const void *fdt, const char *path, 6307e12784SGabe Black const char *prop, const u32 dflt) 643bed2aafSKumar Gala { 653bed2aafSKumar Gala int off; 663bed2aafSKumar Gala 673bed2aafSKumar Gala off = fdt_path_offset(fdt, path); 683bed2aafSKumar Gala if (off < 0) 693bed2aafSKumar Gala return dflt; 703bed2aafSKumar Gala 7194fb182cSAlexander Graf return fdt_getprop_u32_default_node(fdt, off, 0, prop, dflt); 723bed2aafSKumar Gala } 7364dbbd40SGerald Van Baren 74a3c2933eSKumar Gala /** 75a3c2933eSKumar Gala * fdt_find_and_setprop: Find a node and set it's property 76a3c2933eSKumar Gala * 77a3c2933eSKumar Gala * @fdt: ptr to device tree 78a3c2933eSKumar Gala * @node: path of node 79a3c2933eSKumar Gala * @prop: property name 80a3c2933eSKumar Gala * @val: ptr to new value 81a3c2933eSKumar Gala * @len: length of new property value 82a3c2933eSKumar Gala * @create: flag to create the property if it doesn't exist 83a3c2933eSKumar Gala * 84a3c2933eSKumar Gala * Convenience function to directly set a property given the path to the node. 85a3c2933eSKumar Gala */ 86a3c2933eSKumar Gala int fdt_find_and_setprop(void *fdt, const char *node, const char *prop, 87a3c2933eSKumar Gala const void *val, int len, int create) 88a3c2933eSKumar Gala { 898d04f02fSKumar Gala int nodeoff = fdt_path_offset(fdt, node); 90a3c2933eSKumar Gala 91a3c2933eSKumar Gala if (nodeoff < 0) 92a3c2933eSKumar Gala return nodeoff; 93a3c2933eSKumar Gala 948aa5ec6eSKim Phillips if ((!create) && (fdt_get_property(fdt, nodeoff, prop, NULL) == NULL)) 95a3c2933eSKumar Gala return 0; /* create flag not set; so exit quietly */ 96a3c2933eSKumar Gala 97a3c2933eSKumar Gala return fdt_setprop(fdt, nodeoff, prop, val, len); 98a3c2933eSKumar Gala } 99a3c2933eSKumar Gala 1008edb2192SMasahiro Yamada /** 101a9e8e291SSimon Glass * fdt_find_or_add_subnode() - find or possibly add a subnode of a given node 102a9e8e291SSimon Glass * 1038edb2192SMasahiro Yamada * @fdt: pointer to the device tree blob 1048edb2192SMasahiro Yamada * @parentoffset: structure block offset of a node 1058edb2192SMasahiro Yamada * @name: name of the subnode to locate 1068edb2192SMasahiro Yamada * 1078edb2192SMasahiro Yamada * fdt_subnode_offset() finds a subnode of the node with a given name. 1088edb2192SMasahiro Yamada * If the subnode does not exist, it will be created. 1098edb2192SMasahiro Yamada */ 110a9e8e291SSimon Glass int fdt_find_or_add_subnode(void *fdt, int parentoffset, const char *name) 1118edb2192SMasahiro Yamada { 1128edb2192SMasahiro Yamada int offset; 1138edb2192SMasahiro Yamada 1148edb2192SMasahiro Yamada offset = fdt_subnode_offset(fdt, parentoffset, name); 1158edb2192SMasahiro Yamada 1168edb2192SMasahiro Yamada if (offset == -FDT_ERR_NOTFOUND) 1178edb2192SMasahiro Yamada offset = fdt_add_subnode(fdt, parentoffset, name); 1188edb2192SMasahiro Yamada 1198edb2192SMasahiro Yamada if (offset < 0) 1208edb2192SMasahiro Yamada printf("%s: %s: %s\n", __func__, name, fdt_strerror(offset)); 1218edb2192SMasahiro Yamada 1228edb2192SMasahiro Yamada return offset; 1238edb2192SMasahiro Yamada } 1248edb2192SMasahiro Yamada 125972f2a89SMasahiro Yamada /* rename to CONFIG_OF_STDOUT_PATH ? */ 126972f2a89SMasahiro Yamada #if defined(OF_STDOUT_PATH) 127972f2a89SMasahiro Yamada static int fdt_fixup_stdout(void *fdt, int chosenoff) 128972f2a89SMasahiro Yamada { 129972f2a89SMasahiro Yamada return fdt_setprop(fdt, chosenoff, "linux,stdout-path", 130972f2a89SMasahiro Yamada OF_STDOUT_PATH, strlen(OF_STDOUT_PATH) + 1); 131972f2a89SMasahiro Yamada } 132972f2a89SMasahiro Yamada #elif defined(CONFIG_OF_STDOUT_VIA_ALIAS) && defined(CONFIG_CONS_INDEX) 13340777812SDetlev Zundel static int fdt_fixup_stdout(void *fdt, int chosenoff) 134151c8b09SKumar Gala { 135972f2a89SMasahiro Yamada int err; 136972f2a89SMasahiro Yamada int aliasoff; 137151c8b09SKumar Gala char sername[9] = { 0 }; 138972f2a89SMasahiro Yamada const void *path; 139972f2a89SMasahiro Yamada int len; 140972f2a89SMasahiro Yamada char tmp[256]; /* long enough */ 141151c8b09SKumar Gala 142151c8b09SKumar Gala sprintf(sername, "serial%d", CONFIG_CONS_INDEX - 1); 143151c8b09SKumar Gala 144972f2a89SMasahiro Yamada aliasoff = fdt_path_offset(fdt, "/aliases"); 145972f2a89SMasahiro Yamada if (aliasoff < 0) { 146972f2a89SMasahiro Yamada err = aliasoff; 147da77c819SScott Wood goto noalias; 148151c8b09SKumar Gala } 149972f2a89SMasahiro Yamada 150972f2a89SMasahiro Yamada path = fdt_getprop(fdt, aliasoff, sername, &len); 151972f2a89SMasahiro Yamada if (!path) { 152151c8b09SKumar Gala err = len; 153da77c819SScott Wood goto noalias; 154151c8b09SKumar Gala } 155972f2a89SMasahiro Yamada 156972f2a89SMasahiro Yamada /* fdt_setprop may break "path" so we copy it to tmp buffer */ 157972f2a89SMasahiro Yamada memcpy(tmp, path, len); 158972f2a89SMasahiro Yamada 159972f2a89SMasahiro Yamada err = fdt_setprop(fdt, chosenoff, "linux,stdout-path", tmp, len); 160151c8b09SKumar Gala if (err < 0) 161151c8b09SKumar Gala printf("WARNING: could not set linux,stdout-path %s.\n", 162151c8b09SKumar Gala fdt_strerror(err)); 163151c8b09SKumar Gala 164151c8b09SKumar Gala return err; 165da77c819SScott Wood 166da77c819SScott Wood noalias: 167da77c819SScott Wood printf("WARNING: %s: could not read %s alias: %s\n", 168da77c819SScott Wood __func__, sername, fdt_strerror(err)); 169da77c819SScott Wood 170da77c819SScott Wood return 0; 171151c8b09SKumar Gala } 172972f2a89SMasahiro Yamada #else 173972f2a89SMasahiro Yamada static int fdt_fixup_stdout(void *fdt, int chosenoff) 174972f2a89SMasahiro Yamada { 175972f2a89SMasahiro Yamada return 0; 176972f2a89SMasahiro Yamada } 177151c8b09SKumar Gala #endif 178151c8b09SKumar Gala 179f18295d3SMasahiro Yamada static inline int fdt_setprop_uxx(void *fdt, int nodeoffset, const char *name, 180f18295d3SMasahiro Yamada uint64_t val, int is_u64) 181f18295d3SMasahiro Yamada { 182f18295d3SMasahiro Yamada if (is_u64) 183f18295d3SMasahiro Yamada return fdt_setprop_u64(fdt, nodeoffset, name, val); 184f18295d3SMasahiro Yamada else 185f18295d3SMasahiro Yamada return fdt_setprop_u32(fdt, nodeoffset, name, (uint32_t)val); 186f18295d3SMasahiro Yamada } 187f18295d3SMasahiro Yamada 18810be5b5dSPaul Kocialkowski int fdt_root(void *fdt) 18910be5b5dSPaul Kocialkowski { 19010be5b5dSPaul Kocialkowski char *serial; 19110be5b5dSPaul Kocialkowski int err; 19210be5b5dSPaul Kocialkowski 19310be5b5dSPaul Kocialkowski err = fdt_check_header(fdt); 19410be5b5dSPaul Kocialkowski if (err < 0) { 19510be5b5dSPaul Kocialkowski printf("fdt_root: %s\n", fdt_strerror(err)); 19610be5b5dSPaul Kocialkowski return err; 19710be5b5dSPaul Kocialkowski } 19810be5b5dSPaul Kocialkowski 19900caae6dSSimon Glass serial = env_get("serial#"); 20010be5b5dSPaul Kocialkowski if (serial) { 20110be5b5dSPaul Kocialkowski err = fdt_setprop(fdt, 0, "serial-number", serial, 20210be5b5dSPaul Kocialkowski strlen(serial) + 1); 20310be5b5dSPaul Kocialkowski 20410be5b5dSPaul Kocialkowski if (err < 0) { 20510be5b5dSPaul Kocialkowski printf("WARNING: could not set serial-number %s.\n", 20610be5b5dSPaul Kocialkowski fdt_strerror(err)); 20710be5b5dSPaul Kocialkowski return err; 20810be5b5dSPaul Kocialkowski } 20910be5b5dSPaul Kocialkowski } 21010be5b5dSPaul Kocialkowski 21110be5b5dSPaul Kocialkowski return 0; 21210be5b5dSPaul Kocialkowski } 213f18295d3SMasahiro Yamada 214dbe963aeSMasahiro Yamada int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end) 21564dbbd40SGerald Van Baren { 216f18295d3SMasahiro Yamada int nodeoffset; 2172a1a2cb6SKumar Gala int err, j, total; 218f18295d3SMasahiro Yamada int is_u64; 2192a1a2cb6SKumar Gala uint64_t addr, size; 22064dbbd40SGerald Van Baren 22150babaf8SMasahiro Yamada /* just return if the size of initrd is zero */ 22250babaf8SMasahiro Yamada if (initrd_start == initrd_end) 22350babaf8SMasahiro Yamada return 0; 22450babaf8SMasahiro Yamada 2258edb2192SMasahiro Yamada /* find or create "/chosen" node. */ 2268edb2192SMasahiro Yamada nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen"); 2278edb2192SMasahiro Yamada if (nodeoffset < 0) 2282a1a2cb6SKumar Gala return nodeoffset; 22964dbbd40SGerald Van Baren 2302a1a2cb6SKumar Gala total = fdt_num_mem_rsv(fdt); 231c28abb9cSGerald Van Baren 232c28abb9cSGerald Van Baren /* 233c28abb9cSGerald Van Baren * Look for an existing entry and update it. If we don't find 234c28abb9cSGerald Van Baren * the entry, we will j be the next available slot. 235c28abb9cSGerald Van Baren */ 2368d04f02fSKumar Gala for (j = 0; j < total; j++) { 2378d04f02fSKumar Gala err = fdt_get_mem_rsv(fdt, j, &addr, &size); 2388d04f02fSKumar Gala if (addr == initrd_start) { 2398d04f02fSKumar Gala fdt_del_mem_rsv(fdt, j); 240c28abb9cSGerald Van Baren break; 241c28abb9cSGerald Van Baren } 242c28abb9cSGerald Van Baren } 2438d04f02fSKumar Gala 244ce6b27a8SGrant Likely err = fdt_add_mem_rsv(fdt, initrd_start, initrd_end - initrd_start); 24564dbbd40SGerald Van Baren if (err < 0) { 2462a1a2cb6SKumar Gala printf("fdt_initrd: %s\n", fdt_strerror(err)); 24764dbbd40SGerald Van Baren return err; 24864dbbd40SGerald Van Baren } 2492a1a2cb6SKumar Gala 250933cdbb4SSimon Glass is_u64 = (fdt_address_cells(fdt, 0) == 2); 251f77a606aSDavid Feng 252f18295d3SMasahiro Yamada err = fdt_setprop_uxx(fdt, nodeoffset, "linux,initrd-start", 253f18295d3SMasahiro Yamada (uint64_t)initrd_start, is_u64); 254f18295d3SMasahiro Yamada 2552a1a2cb6SKumar Gala if (err < 0) { 256dbe963aeSMasahiro Yamada printf("WARNING: could not set linux,initrd-start %s.\n", 2572a1a2cb6SKumar Gala fdt_strerror(err)); 2582a1a2cb6SKumar Gala return err; 2592a1a2cb6SKumar Gala } 260f18295d3SMasahiro Yamada 261f18295d3SMasahiro Yamada err = fdt_setprop_uxx(fdt, nodeoffset, "linux,initrd-end", 262f18295d3SMasahiro Yamada (uint64_t)initrd_end, is_u64); 263f18295d3SMasahiro Yamada 2642a1a2cb6SKumar Gala if (err < 0) { 2652a1a2cb6SKumar Gala printf("WARNING: could not set linux,initrd-end %s.\n", 2662a1a2cb6SKumar Gala fdt_strerror(err)); 2672a1a2cb6SKumar Gala 2682a1a2cb6SKumar Gala return err; 2692a1a2cb6SKumar Gala } 2702a1a2cb6SKumar Gala 2712a1a2cb6SKumar Gala return 0; 2722a1a2cb6SKumar Gala } 2732a1a2cb6SKumar Gala 274bc6ed0f9SMasahiro Yamada int fdt_chosen(void *fdt) 2752a1a2cb6SKumar Gala { 2762a1a2cb6SKumar Gala int nodeoffset; 2772a1a2cb6SKumar Gala int err; 2782a1a2cb6SKumar Gala char *str; /* used to set string properties */ 2792a1a2cb6SKumar Gala 2802a1a2cb6SKumar Gala err = fdt_check_header(fdt); 2812a1a2cb6SKumar Gala if (err < 0) { 2822a1a2cb6SKumar Gala printf("fdt_chosen: %s\n", fdt_strerror(err)); 2832a1a2cb6SKumar Gala return err; 28464dbbd40SGerald Van Baren } 28564dbbd40SGerald Van Baren 2868edb2192SMasahiro Yamada /* find or create "/chosen" node. */ 2878edb2192SMasahiro Yamada nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen"); 2888edb2192SMasahiro Yamada if (nodeoffset < 0) 28964dbbd40SGerald Van Baren return nodeoffset; 29064dbbd40SGerald Van Baren 29100caae6dSSimon Glass str = env_get("bootargs"); 292972f2a89SMasahiro Yamada if (str) { 293972f2a89SMasahiro Yamada err = fdt_setprop(fdt, nodeoffset, "bootargs", str, 294972f2a89SMasahiro Yamada strlen(str) + 1); 295972f2a89SMasahiro Yamada if (err < 0) { 2965fe6be62SGerald Van Baren printf("WARNING: could not set bootargs %s.\n", 29735ec398fSGerald Van Baren fdt_strerror(err)); 298972f2a89SMasahiro Yamada return err; 299972f2a89SMasahiro Yamada } 30064dbbd40SGerald Van Baren } 3012a1a2cb6SKumar Gala 302972f2a89SMasahiro Yamada return fdt_fixup_stdout(fdt, nodeoffset); 30364dbbd40SGerald Van Baren } 30464dbbd40SGerald Van Baren 305e93becf8SKumar Gala void do_fixup_by_path(void *fdt, const char *path, const char *prop, 306e93becf8SKumar Gala const void *val, int len, int create) 307e93becf8SKumar Gala { 308e93becf8SKumar Gala #if defined(DEBUG) 309e93becf8SKumar Gala int i; 310d9ad115bSKumar Gala debug("Updating property '%s/%s' = ", path, prop); 311e93becf8SKumar Gala for (i = 0; i < len; i++) 312e93becf8SKumar Gala debug(" %.2x", *(u8*)(val+i)); 313e93becf8SKumar Gala debug("\n"); 314e93becf8SKumar Gala #endif 315e93becf8SKumar Gala int rc = fdt_find_and_setprop(fdt, path, prop, val, len, create); 316e93becf8SKumar Gala if (rc) 317e93becf8SKumar Gala printf("Unable to update property %s:%s, err=%s\n", 318e93becf8SKumar Gala path, prop, fdt_strerror(rc)); 319e93becf8SKumar Gala } 320e93becf8SKumar Gala 321e93becf8SKumar Gala void do_fixup_by_path_u32(void *fdt, const char *path, const char *prop, 322e93becf8SKumar Gala u32 val, int create) 323e93becf8SKumar Gala { 3248aa5ec6eSKim Phillips fdt32_t tmp = cpu_to_fdt32(val); 3258aa5ec6eSKim Phillips do_fixup_by_path(fdt, path, prop, &tmp, sizeof(tmp), create); 326e93becf8SKumar Gala } 327e93becf8SKumar Gala 3289eb77ceaSKumar Gala void do_fixup_by_prop(void *fdt, 3299eb77ceaSKumar Gala const char *pname, const void *pval, int plen, 3309eb77ceaSKumar Gala const char *prop, const void *val, int len, 3319eb77ceaSKumar Gala int create) 3329eb77ceaSKumar Gala { 3339eb77ceaSKumar Gala int off; 3349eb77ceaSKumar Gala #if defined(DEBUG) 3359eb77ceaSKumar Gala int i; 336d9ad115bSKumar Gala debug("Updating property '%s' = ", prop); 3379eb77ceaSKumar Gala for (i = 0; i < len; i++) 3389eb77ceaSKumar Gala debug(" %.2x", *(u8*)(val+i)); 3399eb77ceaSKumar Gala debug("\n"); 3409eb77ceaSKumar Gala #endif 3419eb77ceaSKumar Gala off = fdt_node_offset_by_prop_value(fdt, -1, pname, pval, plen); 3429eb77ceaSKumar Gala while (off != -FDT_ERR_NOTFOUND) { 3438aa5ec6eSKim Phillips if (create || (fdt_get_property(fdt, off, prop, NULL) != NULL)) 3449eb77ceaSKumar Gala fdt_setprop(fdt, off, prop, val, len); 3459eb77ceaSKumar Gala off = fdt_node_offset_by_prop_value(fdt, off, pname, pval, plen); 3469eb77ceaSKumar Gala } 3479eb77ceaSKumar Gala } 3489eb77ceaSKumar Gala 3499eb77ceaSKumar Gala void do_fixup_by_prop_u32(void *fdt, 3509eb77ceaSKumar Gala const char *pname, const void *pval, int plen, 3519eb77ceaSKumar Gala const char *prop, u32 val, int create) 3529eb77ceaSKumar Gala { 3538aa5ec6eSKim Phillips fdt32_t tmp = cpu_to_fdt32(val); 3548aa5ec6eSKim Phillips do_fixup_by_prop(fdt, pname, pval, plen, prop, &tmp, 4, create); 3559eb77ceaSKumar Gala } 3569eb77ceaSKumar Gala 3579eb77ceaSKumar Gala void do_fixup_by_compat(void *fdt, const char *compat, 3589eb77ceaSKumar Gala const char *prop, const void *val, int len, int create) 3599eb77ceaSKumar Gala { 3609eb77ceaSKumar Gala int off = -1; 3619eb77ceaSKumar Gala #if defined(DEBUG) 3629eb77ceaSKumar Gala int i; 363d9ad115bSKumar Gala debug("Updating property '%s' = ", prop); 3649eb77ceaSKumar Gala for (i = 0; i < len; i++) 3659eb77ceaSKumar Gala debug(" %.2x", *(u8*)(val+i)); 3669eb77ceaSKumar Gala debug("\n"); 3679eb77ceaSKumar Gala #endif 3689eb77ceaSKumar Gala off = fdt_node_offset_by_compatible(fdt, -1, compat); 3699eb77ceaSKumar Gala while (off != -FDT_ERR_NOTFOUND) { 3708aa5ec6eSKim Phillips if (create || (fdt_get_property(fdt, off, prop, NULL) != NULL)) 3719eb77ceaSKumar Gala fdt_setprop(fdt, off, prop, val, len); 3729eb77ceaSKumar Gala off = fdt_node_offset_by_compatible(fdt, off, compat); 3739eb77ceaSKumar Gala } 3749eb77ceaSKumar Gala } 3759eb77ceaSKumar Gala 3769eb77ceaSKumar Gala void do_fixup_by_compat_u32(void *fdt, const char *compat, 3779eb77ceaSKumar Gala const char *prop, u32 val, int create) 3789eb77ceaSKumar Gala { 3798aa5ec6eSKim Phillips fdt32_t tmp = cpu_to_fdt32(val); 3808aa5ec6eSKim Phillips do_fixup_by_compat(fdt, compat, prop, &tmp, 4, create); 3819eb77ceaSKumar Gala } 3829eb77ceaSKumar Gala 38363c09417SMasahiro Yamada #ifdef CONFIG_ARCH_FIXUP_FDT_MEMORY 384739a01edSMasahiro Yamada /* 385739a01edSMasahiro Yamada * fdt_pack_reg - pack address and size array into the "reg"-suitable stream 386739a01edSMasahiro Yamada */ 38741f09bbeSSimon Glass static int fdt_pack_reg(const void *fdt, void *buf, u64 *address, u64 *size, 38841f09bbeSSimon Glass int n) 389739a01edSMasahiro Yamada { 390739a01edSMasahiro Yamada int i; 391ffccb84cSHans de Goede int address_cells = fdt_address_cells(fdt, 0); 392ffccb84cSHans de Goede int size_cells = fdt_size_cells(fdt, 0); 393739a01edSMasahiro Yamada char *p = buf; 394739a01edSMasahiro Yamada 395739a01edSMasahiro Yamada for (i = 0; i < n; i++) { 396ffccb84cSHans de Goede if (address_cells == 2) 397739a01edSMasahiro Yamada *(fdt64_t *)p = cpu_to_fdt64(address[i]); 398739a01edSMasahiro Yamada else 399739a01edSMasahiro Yamada *(fdt32_t *)p = cpu_to_fdt32(address[i]); 400ffccb84cSHans de Goede p += 4 * address_cells; 401739a01edSMasahiro Yamada 402ffccb84cSHans de Goede if (size_cells == 2) 403739a01edSMasahiro Yamada *(fdt64_t *)p = cpu_to_fdt64(size[i]); 404739a01edSMasahiro Yamada else 405739a01edSMasahiro Yamada *(fdt32_t *)p = cpu_to_fdt32(size[i]); 406ffccb84cSHans de Goede p += 4 * size_cells; 407739a01edSMasahiro Yamada } 408739a01edSMasahiro Yamada 409739a01edSMasahiro Yamada return p - (char *)buf; 410739a01edSMasahiro Yamada } 411739a01edSMasahiro Yamada 412*6b6f216fSRamon Fried #if CONFIG_NR_DRAM_BANKS > 4 413*6b6f216fSRamon Fried #define MEMORY_BANKS_MAX CONFIG_NR_DRAM_BANKS 414*6b6f216fSRamon Fried #else 4158aa5ec6eSKim Phillips #define MEMORY_BANKS_MAX 4 416*6b6f216fSRamon Fried #endif 417a6bd9e83SJohn Rigby int fdt_fixup_memory_banks(void *blob, u64 start[], u64 size[], int banks) 418a6bd9e83SJohn Rigby { 419a6bd9e83SJohn Rigby int err, nodeoffset; 4206d29cc7dSThierry Reding int len, i; 4218aa5ec6eSKim Phillips u8 tmp[MEMORY_BANKS_MAX * 16]; /* Up to 64-bit address + 64-bit size */ 4223c927281SKumar Gala 4238aa5ec6eSKim Phillips if (banks > MEMORY_BANKS_MAX) { 4248aa5ec6eSKim Phillips printf("%s: num banks %d exceeds hardcoded limit %d." 4258aa5ec6eSKim Phillips " Recompile with higher MEMORY_BANKS_MAX?\n", 4268aa5ec6eSKim Phillips __FUNCTION__, banks, MEMORY_BANKS_MAX); 4278aa5ec6eSKim Phillips return -1; 4288aa5ec6eSKim Phillips } 4298aa5ec6eSKim Phillips 4303c927281SKumar Gala err = fdt_check_header(blob); 4313c927281SKumar Gala if (err < 0) { 4323c927281SKumar Gala printf("%s: %s\n", __FUNCTION__, fdt_strerror(err)); 4333c927281SKumar Gala return err; 4343c927281SKumar Gala } 4353c927281SKumar Gala 4368edb2192SMasahiro Yamada /* find or create "/memory" node. */ 4378edb2192SMasahiro Yamada nodeoffset = fdt_find_or_add_subnode(blob, 0, "memory"); 4388edb2192SMasahiro Yamada if (nodeoffset < 0) 4393c927281SKumar Gala return nodeoffset; 4408edb2192SMasahiro Yamada 4413c927281SKumar Gala err = fdt_setprop(blob, nodeoffset, "device_type", "memory", 4423c927281SKumar Gala sizeof("memory")); 4433c927281SKumar Gala if (err < 0) { 4443c927281SKumar Gala printf("WARNING: could not set %s %s.\n", "device_type", 4453c927281SKumar Gala fdt_strerror(err)); 4463c927281SKumar Gala return err; 4473c927281SKumar Gala } 4483c927281SKumar Gala 449ed5af03fSThierry Reding for (i = 0; i < banks; i++) { 450ed5af03fSThierry Reding if (start[i] == 0 && size[i] == 0) 451ed5af03fSThierry Reding break; 452ed5af03fSThierry Reding } 453ed5af03fSThierry Reding 454ed5af03fSThierry Reding banks = i; 455ed5af03fSThierry Reding 4565c1cf89fSAndre Przywara if (!banks) 4575c1cf89fSAndre Przywara return 0; 4585c1cf89fSAndre Przywara 4596d29cc7dSThierry Reding for (i = 0; i < banks; i++) 4606d29cc7dSThierry Reding if (start[i] == 0 && size[i] == 0) 4616d29cc7dSThierry Reding break; 4626d29cc7dSThierry Reding 4636d29cc7dSThierry Reding banks = i; 4646d29cc7dSThierry Reding 465739a01edSMasahiro Yamada len = fdt_pack_reg(blob, tmp, start, size, banks); 4663c927281SKumar Gala 4673c927281SKumar Gala err = fdt_setprop(blob, nodeoffset, "reg", tmp, len); 4683c927281SKumar Gala if (err < 0) { 4693c927281SKumar Gala printf("WARNING: could not set %s %s.\n", 4703c927281SKumar Gala "reg", fdt_strerror(err)); 4713c927281SKumar Gala return err; 4723c927281SKumar Gala } 4733c927281SKumar Gala return 0; 4743c927281SKumar Gala } 47563c09417SMasahiro Yamada #endif 4763c927281SKumar Gala 477a6bd9e83SJohn Rigby int fdt_fixup_memory(void *blob, u64 start, u64 size) 478a6bd9e83SJohn Rigby { 479a6bd9e83SJohn Rigby return fdt_fixup_memory_banks(blob, &start, &size, 1); 480a6bd9e83SJohn Rigby } 481a6bd9e83SJohn Rigby 482ba37aa03SKumar Gala void fdt_fixup_ethernet(void *fdt) 483ab544633SKumar Gala { 48424acb83dSPrabhakar Kushwaha int i = 0, j, prop; 485bc393a79SBin Meng char *tmp, *end; 486064d55f8SStephen Warren char mac[16]; 487ab544633SKumar Gala const char *path; 488a40db6d5Soliver@schinagl.nl unsigned char mac_addr[ARP_HLEN]; 489bc393a79SBin Meng int offset; 49024acb83dSPrabhakar Kushwaha #ifdef FDT_SEQ_MACADDR_FROM_ENV 49124acb83dSPrabhakar Kushwaha int nodeoff; 49224acb83dSPrabhakar Kushwaha const struct fdt_property *fdt_prop; 49324acb83dSPrabhakar Kushwaha #endif 494ab544633SKumar Gala 495a434fd1dSLev Iserovich if (fdt_path_offset(fdt, "/aliases") < 0) 496ba37aa03SKumar Gala return; 497ba37aa03SKumar Gala 498a434fd1dSLev Iserovich /* Cycle through all aliases */ 499a434fd1dSLev Iserovich for (prop = 0; ; prop++) { 500bc393a79SBin Meng const char *name; 501b1f49ab8SDan Murphy 502a434fd1dSLev Iserovich /* FDT might have been edited, recompute the offset */ 503a434fd1dSLev Iserovich offset = fdt_first_property_offset(fdt, 504a434fd1dSLev Iserovich fdt_path_offset(fdt, "/aliases")); 505a434fd1dSLev Iserovich /* Select property number 'prop' */ 50624acb83dSPrabhakar Kushwaha for (j = 0; j < prop; j++) 507a434fd1dSLev Iserovich offset = fdt_next_property_offset(fdt, offset); 508a434fd1dSLev Iserovich 509a434fd1dSLev Iserovich if (offset < 0) 510a434fd1dSLev Iserovich break; 511a434fd1dSLev Iserovich 512bc393a79SBin Meng path = fdt_getprop_by_offset(fdt, offset, &name, NULL); 513f8e57c65STuomas Tynkkynen if (!strncmp(name, "ethernet", 8)) { 514f8e57c65STuomas Tynkkynen /* Treat plain "ethernet" same as "ethernet0". */ 51524acb83dSPrabhakar Kushwaha if (!strcmp(name, "ethernet") 51624acb83dSPrabhakar Kushwaha #ifdef FDT_SEQ_MACADDR_FROM_ENV 51724acb83dSPrabhakar Kushwaha || !strcmp(name, "ethernet0") 51824acb83dSPrabhakar Kushwaha #endif 51924acb83dSPrabhakar Kushwaha ) 520f8e57c65STuomas Tynkkynen i = 0; 52124acb83dSPrabhakar Kushwaha #ifndef FDT_SEQ_MACADDR_FROM_ENV 522f8e57c65STuomas Tynkkynen else 523bc393a79SBin Meng i = trailing_strtol(name); 52424acb83dSPrabhakar Kushwaha #endif 525bc393a79SBin Meng if (i != -1) { 526bc393a79SBin Meng if (i == 0) 52752d825ccSBin Meng strcpy(mac, "ethaddr"); 528bc393a79SBin Meng else 529bc393a79SBin Meng sprintf(mac, "eth%daddr", i); 530bc393a79SBin Meng } else { 531ba37aa03SKumar Gala continue; 532ba37aa03SKumar Gala } 53324acb83dSPrabhakar Kushwaha #ifdef FDT_SEQ_MACADDR_FROM_ENV 53424acb83dSPrabhakar Kushwaha nodeoff = fdt_path_offset(fdt, path); 53524acb83dSPrabhakar Kushwaha fdt_prop = fdt_get_property(fdt, nodeoff, "status", 53624acb83dSPrabhakar Kushwaha NULL); 53724acb83dSPrabhakar Kushwaha if (fdt_prop && !strcmp(fdt_prop->data, "disabled")) 53824acb83dSPrabhakar Kushwaha continue; 53924acb83dSPrabhakar Kushwaha i++; 54024acb83dSPrabhakar Kushwaha #endif 54100caae6dSSimon Glass tmp = env_get(mac); 542bc393a79SBin Meng if (!tmp) 543bc393a79SBin Meng continue; 544ba37aa03SKumar Gala 545ba37aa03SKumar Gala for (j = 0; j < 6; j++) { 546bc393a79SBin Meng mac_addr[j] = tmp ? 547bc393a79SBin Meng simple_strtoul(tmp, &end, 16) : 0; 548ba37aa03SKumar Gala if (tmp) 549ba37aa03SKumar Gala tmp = (*end) ? end + 1 : end; 550ba37aa03SKumar Gala } 551ba37aa03SKumar Gala 552bc393a79SBin Meng do_fixup_by_path(fdt, path, "mac-address", 553bc393a79SBin Meng &mac_addr, 6, 0); 554ab544633SKumar Gala do_fixup_by_path(fdt, path, "local-mac-address", 555ba37aa03SKumar Gala &mac_addr, 6, 1); 556bc393a79SBin Meng } 557ab544633SKumar Gala } 558ab544633SKumar Gala } 55918e69a35SAnton Vorontsov 560a5af51a7SPhilipp Tomsich int fdt_record_loadable(void *blob, u32 index, const char *name, 561a5af51a7SPhilipp Tomsich uintptr_t load_addr, u32 size, uintptr_t entry_point, 562a5af51a7SPhilipp Tomsich const char *type, const char *os) 563a5af51a7SPhilipp Tomsich { 564a5af51a7SPhilipp Tomsich int err, node; 565a5af51a7SPhilipp Tomsich 566a5af51a7SPhilipp Tomsich err = fdt_check_header(blob); 567a5af51a7SPhilipp Tomsich if (err < 0) { 568a5af51a7SPhilipp Tomsich printf("%s: %s\n", __func__, fdt_strerror(err)); 569a5af51a7SPhilipp Tomsich return err; 570a5af51a7SPhilipp Tomsich } 571a5af51a7SPhilipp Tomsich 572a5af51a7SPhilipp Tomsich /* find or create "/fit-images" node */ 573a5af51a7SPhilipp Tomsich node = fdt_find_or_add_subnode(blob, 0, "fit-images"); 574a5af51a7SPhilipp Tomsich if (node < 0) 575a5af51a7SPhilipp Tomsich return node; 576a5af51a7SPhilipp Tomsich 577a5af51a7SPhilipp Tomsich /* find or create "/fit-images/<name>" node */ 578a5af51a7SPhilipp Tomsich node = fdt_find_or_add_subnode(blob, node, name); 579a5af51a7SPhilipp Tomsich if (node < 0) 580a5af51a7SPhilipp Tomsich return node; 581a5af51a7SPhilipp Tomsich 582a5af51a7SPhilipp Tomsich /* 583a5af51a7SPhilipp Tomsich * We record these as 32bit entities, possibly truncating addresses. 584a5af51a7SPhilipp Tomsich * However, spl_fit.c is not 64bit safe either: i.e. we should not 585a5af51a7SPhilipp Tomsich * have an issue here. 586a5af51a7SPhilipp Tomsich */ 587a5af51a7SPhilipp Tomsich fdt_setprop_u32(blob, node, "load-addr", load_addr); 588a5af51a7SPhilipp Tomsich if (entry_point != -1) 589a5af51a7SPhilipp Tomsich fdt_setprop_u32(blob, node, "entry-point", entry_point); 590a5af51a7SPhilipp Tomsich fdt_setprop_u32(blob, node, "size", size); 591a5af51a7SPhilipp Tomsich if (type) 592a5af51a7SPhilipp Tomsich fdt_setprop_string(blob, node, "type", type); 593a5af51a7SPhilipp Tomsich if (os) 594a5af51a7SPhilipp Tomsich fdt_setprop_string(blob, node, "os", os); 595a5af51a7SPhilipp Tomsich 596a5af51a7SPhilipp Tomsich return node; 597a5af51a7SPhilipp Tomsich } 598a5af51a7SPhilipp Tomsich 5993082d234SKumar Gala /* Resize the fdt to its actual size + a bit of padding */ 600ef476836SHannes Schmelzer int fdt_shrink_to_minimum(void *blob, uint extrasize) 6013082d234SKumar Gala { 6023082d234SKumar Gala int i; 6033082d234SKumar Gala uint64_t addr, size; 6043082d234SKumar Gala int total, ret; 6053082d234SKumar Gala uint actualsize; 6063082d234SKumar Gala 6073082d234SKumar Gala if (!blob) 6083082d234SKumar Gala return 0; 6093082d234SKumar Gala 6103082d234SKumar Gala total = fdt_num_mem_rsv(blob); 6113082d234SKumar Gala for (i = 0; i < total; i++) { 6123082d234SKumar Gala fdt_get_mem_rsv(blob, i, &addr, &size); 61392549358SSimon Glass if (addr == (uintptr_t)blob) { 6143082d234SKumar Gala fdt_del_mem_rsv(blob, i); 6153082d234SKumar Gala break; 6163082d234SKumar Gala } 6173082d234SKumar Gala } 6183082d234SKumar Gala 619f242a088SPeter Korsgaard /* 620f242a088SPeter Korsgaard * Calculate the actual size of the fdt 6213840ebfaSFeng Wang * plus the size needed for 5 fdt_add_mem_rsv, one 6223840ebfaSFeng Wang * for the fdt itself and 4 for a possible initrd 6233840ebfaSFeng Wang * ((initrd-start + initrd-end) * 2 (name & value)) 624f242a088SPeter Korsgaard */ 6253082d234SKumar Gala actualsize = fdt_off_dt_strings(blob) + 6263840ebfaSFeng Wang fdt_size_dt_strings(blob) + 5 * sizeof(struct fdt_reserve_entry); 6273082d234SKumar Gala 628ef476836SHannes Schmelzer actualsize += extrasize; 6293082d234SKumar Gala /* Make it so the fdt ends on a page boundary */ 63092549358SSimon Glass actualsize = ALIGN(actualsize + ((uintptr_t)blob & 0xfff), 0x1000); 63192549358SSimon Glass actualsize = actualsize - ((uintptr_t)blob & 0xfff); 6323082d234SKumar Gala 6333082d234SKumar Gala /* Change the fdt header to reflect the correct size */ 6343082d234SKumar Gala fdt_set_totalsize(blob, actualsize); 6353082d234SKumar Gala 6363082d234SKumar Gala /* Add the new reservation */ 63792549358SSimon Glass ret = fdt_add_mem_rsv(blob, (uintptr_t)blob, actualsize); 6383082d234SKumar Gala if (ret < 0) 6393082d234SKumar Gala return ret; 6403082d234SKumar Gala 6413082d234SKumar Gala return actualsize; 6423082d234SKumar Gala } 6438ab451c4SKumar Gala 6448ab451c4SKumar Gala #ifdef CONFIG_PCI 645cfd700beSKumar Gala #define CONFIG_SYS_PCI_NR_INBOUND_WIN 4 6468ab451c4SKumar Gala 6478ab451c4SKumar Gala #define FDT_PCI_PREFETCH (0x40000000) 6488ab451c4SKumar Gala #define FDT_PCI_MEM32 (0x02000000) 6498ab451c4SKumar Gala #define FDT_PCI_IO (0x01000000) 6508ab451c4SKumar Gala #define FDT_PCI_MEM64 (0x03000000) 6518ab451c4SKumar Gala 6528ab451c4SKumar Gala int fdt_pci_dma_ranges(void *blob, int phb_off, struct pci_controller *hose) { 6538ab451c4SKumar Gala 6548ab451c4SKumar Gala int addrcell, sizecell, len, r; 6558ab451c4SKumar Gala u32 *dma_range; 6568ab451c4SKumar Gala /* sized based on pci addr cells, size-cells, & address-cells */ 6578ab451c4SKumar Gala u32 dma_ranges[(3 + 2 + 2) * CONFIG_SYS_PCI_NR_INBOUND_WIN]; 6588ab451c4SKumar Gala 6598ab451c4SKumar Gala addrcell = fdt_getprop_u32_default(blob, "/", "#address-cells", 1); 6608ab451c4SKumar Gala sizecell = fdt_getprop_u32_default(blob, "/", "#size-cells", 1); 6618ab451c4SKumar Gala 6628ab451c4SKumar Gala dma_range = &dma_ranges[0]; 6638ab451c4SKumar Gala for (r = 0; r < hose->region_count; r++) { 6648ab451c4SKumar Gala u64 bus_start, phys_start, size; 6658ab451c4SKumar Gala 666ff4e66e9SKumar Gala /* skip if !PCI_REGION_SYS_MEMORY */ 667ff4e66e9SKumar Gala if (!(hose->regions[r].flags & PCI_REGION_SYS_MEMORY)) 6688ab451c4SKumar Gala continue; 6698ab451c4SKumar Gala 6708ab451c4SKumar Gala bus_start = (u64)hose->regions[r].bus_start; 6718ab451c4SKumar Gala phys_start = (u64)hose->regions[r].phys_start; 6728ab451c4SKumar Gala size = (u64)hose->regions[r].size; 6738ab451c4SKumar Gala 6748ab451c4SKumar Gala dma_range[0] = 0; 675cfd700beSKumar Gala if (size >= 0x100000000ull) 6768ab451c4SKumar Gala dma_range[0] |= FDT_PCI_MEM64; 6778ab451c4SKumar Gala else 6788ab451c4SKumar Gala dma_range[0] |= FDT_PCI_MEM32; 6798ab451c4SKumar Gala if (hose->regions[r].flags & PCI_REGION_PREFETCH) 6808ab451c4SKumar Gala dma_range[0] |= FDT_PCI_PREFETCH; 6818ab451c4SKumar Gala #ifdef CONFIG_SYS_PCI_64BIT 6828ab451c4SKumar Gala dma_range[1] = bus_start >> 32; 6838ab451c4SKumar Gala #else 6848ab451c4SKumar Gala dma_range[1] = 0; 6858ab451c4SKumar Gala #endif 6868ab451c4SKumar Gala dma_range[2] = bus_start & 0xffffffff; 6878ab451c4SKumar Gala 6888ab451c4SKumar Gala if (addrcell == 2) { 6898ab451c4SKumar Gala dma_range[3] = phys_start >> 32; 6908ab451c4SKumar Gala dma_range[4] = phys_start & 0xffffffff; 6918ab451c4SKumar Gala } else { 6928ab451c4SKumar Gala dma_range[3] = phys_start & 0xffffffff; 6938ab451c4SKumar Gala } 6948ab451c4SKumar Gala 6958ab451c4SKumar Gala if (sizecell == 2) { 6968ab451c4SKumar Gala dma_range[3 + addrcell + 0] = size >> 32; 6978ab451c4SKumar Gala dma_range[3 + addrcell + 1] = size & 0xffffffff; 6988ab451c4SKumar Gala } else { 6998ab451c4SKumar Gala dma_range[3 + addrcell + 0] = size & 0xffffffff; 7008ab451c4SKumar Gala } 7018ab451c4SKumar Gala 7028ab451c4SKumar Gala dma_range += (3 + addrcell + sizecell); 7038ab451c4SKumar Gala } 7048ab451c4SKumar Gala 7058ab451c4SKumar Gala len = dma_range - &dma_ranges[0]; 7068ab451c4SKumar Gala if (len) 7078ab451c4SKumar Gala fdt_setprop(blob, phb_off, "dma-ranges", &dma_ranges[0], len*4); 7088ab451c4SKumar Gala 7098ab451c4SKumar Gala return 0; 7108ab451c4SKumar Gala } 7118ab451c4SKumar Gala #endif 71230d45c0dSStefan Roese 713cb2707afSMatthew McClintock int fdt_increase_size(void *fdt, int add_len) 714cb2707afSMatthew McClintock { 715cb2707afSMatthew McClintock int newlen; 716cb2707afSMatthew McClintock 717cb2707afSMatthew McClintock newlen = fdt_totalsize(fdt) + add_len; 718cb2707afSMatthew McClintock 719cb2707afSMatthew McClintock /* Open in place with a new len */ 720cb2707afSMatthew McClintock return fdt_open_into(fdt, fdt, newlen); 721cb2707afSMatthew McClintock } 722cb2707afSMatthew McClintock 7233c950e2eSAnatolij Gustschin #ifdef CONFIG_FDT_FIXUP_PARTITIONS 7243c950e2eSAnatolij Gustschin #include <jffs2/load_kernel.h> 7253c950e2eSAnatolij Gustschin #include <mtd_node.h> 7263c950e2eSAnatolij Gustschin 7273c950e2eSAnatolij Gustschin struct reg_cell { 7283c950e2eSAnatolij Gustschin unsigned int r0; 7293c950e2eSAnatolij Gustschin unsigned int r1; 7303c950e2eSAnatolij Gustschin }; 7313c950e2eSAnatolij Gustschin 732cd1457d7SMichal Simek static int fdt_del_subnodes(const void *blob, int parent_offset) 7333c950e2eSAnatolij Gustschin { 7343c950e2eSAnatolij Gustschin int off, ndepth; 7353c950e2eSAnatolij Gustschin int ret; 7363c950e2eSAnatolij Gustschin 7373c950e2eSAnatolij Gustschin for (ndepth = 0, off = fdt_next_node(blob, parent_offset, &ndepth); 7383c950e2eSAnatolij Gustschin (off >= 0) && (ndepth > 0); 7393c950e2eSAnatolij Gustschin off = fdt_next_node(blob, off, &ndepth)) { 7403c950e2eSAnatolij Gustschin if (ndepth == 1) { 7413c950e2eSAnatolij Gustschin debug("delete %s: offset: %x\n", 7423c950e2eSAnatolij Gustschin fdt_get_name(blob, off, 0), off); 7433c950e2eSAnatolij Gustschin ret = fdt_del_node((void *)blob, off); 7443c950e2eSAnatolij Gustschin if (ret < 0) { 7453c950e2eSAnatolij Gustschin printf("Can't delete node: %s\n", 7463c950e2eSAnatolij Gustschin fdt_strerror(ret)); 7473c950e2eSAnatolij Gustschin return ret; 7483c950e2eSAnatolij Gustschin } else { 7493c950e2eSAnatolij Gustschin ndepth = 0; 7503c950e2eSAnatolij Gustschin off = parent_offset; 7513c950e2eSAnatolij Gustschin } 7523c950e2eSAnatolij Gustschin } 7533c950e2eSAnatolij Gustschin } 7543c950e2eSAnatolij Gustschin return 0; 7553c950e2eSAnatolij Gustschin } 7563c950e2eSAnatolij Gustschin 757cd1457d7SMichal Simek static int fdt_del_partitions(void *blob, int parent_offset) 7583c950e2eSAnatolij Gustschin { 7593c950e2eSAnatolij Gustschin const void *prop; 7603c950e2eSAnatolij Gustschin int ndepth = 0; 7613c950e2eSAnatolij Gustschin int off; 7623c950e2eSAnatolij Gustschin int ret; 7633c950e2eSAnatolij Gustschin 7643c950e2eSAnatolij Gustschin off = fdt_next_node(blob, parent_offset, &ndepth); 7653c950e2eSAnatolij Gustschin if (off > 0 && ndepth == 1) { 7663c950e2eSAnatolij Gustschin prop = fdt_getprop(blob, off, "label", NULL); 7673c950e2eSAnatolij Gustschin if (prop == NULL) { 7683c950e2eSAnatolij Gustschin /* 7693c950e2eSAnatolij Gustschin * Could not find label property, nand {}; node? 7703c950e2eSAnatolij Gustschin * Check subnode, delete partitions there if any. 7713c950e2eSAnatolij Gustschin */ 7723c950e2eSAnatolij Gustschin return fdt_del_partitions(blob, off); 7733c950e2eSAnatolij Gustschin } else { 7743c950e2eSAnatolij Gustschin ret = fdt_del_subnodes(blob, parent_offset); 7753c950e2eSAnatolij Gustschin if (ret < 0) { 7763c950e2eSAnatolij Gustschin printf("Can't remove subnodes: %s\n", 7773c950e2eSAnatolij Gustschin fdt_strerror(ret)); 7783c950e2eSAnatolij Gustschin return ret; 7793c950e2eSAnatolij Gustschin } 7803c950e2eSAnatolij Gustschin } 7813c950e2eSAnatolij Gustschin } 7823c950e2eSAnatolij Gustschin return 0; 7833c950e2eSAnatolij Gustschin } 7843c950e2eSAnatolij Gustschin 7853c950e2eSAnatolij Gustschin int fdt_node_set_part_info(void *blob, int parent_offset, 7863c950e2eSAnatolij Gustschin struct mtd_device *dev) 7873c950e2eSAnatolij Gustschin { 7883c950e2eSAnatolij Gustschin struct list_head *pentry; 7893c950e2eSAnatolij Gustschin struct part_info *part; 7903c950e2eSAnatolij Gustschin struct reg_cell cell; 7913c950e2eSAnatolij Gustschin int off, ndepth = 0; 7923c950e2eSAnatolij Gustschin int part_num, ret; 7933c950e2eSAnatolij Gustschin char buf[64]; 7943c950e2eSAnatolij Gustschin 7953c950e2eSAnatolij Gustschin ret = fdt_del_partitions(blob, parent_offset); 7963c950e2eSAnatolij Gustschin if (ret < 0) 7973c950e2eSAnatolij Gustschin return ret; 7983c950e2eSAnatolij Gustschin 7993c950e2eSAnatolij Gustschin /* 8003c950e2eSAnatolij Gustschin * Check if it is nand {}; subnode, adjust 8013c950e2eSAnatolij Gustschin * the offset in this case 8023c950e2eSAnatolij Gustschin */ 8033c950e2eSAnatolij Gustschin off = fdt_next_node(blob, parent_offset, &ndepth); 8043c950e2eSAnatolij Gustschin if (off > 0 && ndepth == 1) 8053c950e2eSAnatolij Gustschin parent_offset = off; 8063c950e2eSAnatolij Gustschin 8073c950e2eSAnatolij Gustschin part_num = 0; 8083c950e2eSAnatolij Gustschin list_for_each_prev(pentry, &dev->parts) { 8093c950e2eSAnatolij Gustschin int newoff; 8103c950e2eSAnatolij Gustschin 8113c950e2eSAnatolij Gustschin part = list_entry(pentry, struct part_info, link); 8123c950e2eSAnatolij Gustschin 81306503f16SScott Wood debug("%2d: %-20s0x%08llx\t0x%08llx\t%d\n", 8143c950e2eSAnatolij Gustschin part_num, part->name, part->size, 8153c950e2eSAnatolij Gustschin part->offset, part->mask_flags); 8163c950e2eSAnatolij Gustschin 81706503f16SScott Wood sprintf(buf, "partition@%llx", part->offset); 8183c950e2eSAnatolij Gustschin add_sub: 8193c950e2eSAnatolij Gustschin ret = fdt_add_subnode(blob, parent_offset, buf); 8203c950e2eSAnatolij Gustschin if (ret == -FDT_ERR_NOSPACE) { 8213c950e2eSAnatolij Gustschin ret = fdt_increase_size(blob, 512); 8223c950e2eSAnatolij Gustschin if (!ret) 8233c950e2eSAnatolij Gustschin goto add_sub; 8243c950e2eSAnatolij Gustschin else 8253c950e2eSAnatolij Gustschin goto err_size; 8263c950e2eSAnatolij Gustschin } else if (ret < 0) { 8273c950e2eSAnatolij Gustschin printf("Can't add partition node: %s\n", 8283c950e2eSAnatolij Gustschin fdt_strerror(ret)); 8293c950e2eSAnatolij Gustschin return ret; 8303c950e2eSAnatolij Gustschin } 8313c950e2eSAnatolij Gustschin newoff = ret; 8323c950e2eSAnatolij Gustschin 8333c950e2eSAnatolij Gustschin /* Check MTD_WRITEABLE_CMD flag */ 8343c950e2eSAnatolij Gustschin if (part->mask_flags & 1) { 8353c950e2eSAnatolij Gustschin add_ro: 8363c950e2eSAnatolij Gustschin ret = fdt_setprop(blob, newoff, "read_only", NULL, 0); 8373c950e2eSAnatolij Gustschin if (ret == -FDT_ERR_NOSPACE) { 8383c950e2eSAnatolij Gustschin ret = fdt_increase_size(blob, 512); 8393c950e2eSAnatolij Gustschin if (!ret) 8403c950e2eSAnatolij Gustschin goto add_ro; 8413c950e2eSAnatolij Gustschin else 8423c950e2eSAnatolij Gustschin goto err_size; 8433c950e2eSAnatolij Gustschin } else if (ret < 0) 8443c950e2eSAnatolij Gustschin goto err_prop; 8453c950e2eSAnatolij Gustschin } 8463c950e2eSAnatolij Gustschin 8473c950e2eSAnatolij Gustschin cell.r0 = cpu_to_fdt32(part->offset); 8483c950e2eSAnatolij Gustschin cell.r1 = cpu_to_fdt32(part->size); 8493c950e2eSAnatolij Gustschin add_reg: 8503c950e2eSAnatolij Gustschin ret = fdt_setprop(blob, newoff, "reg", &cell, sizeof(cell)); 8513c950e2eSAnatolij Gustschin if (ret == -FDT_ERR_NOSPACE) { 8523c950e2eSAnatolij Gustschin ret = fdt_increase_size(blob, 512); 8533c950e2eSAnatolij Gustschin if (!ret) 8543c950e2eSAnatolij Gustschin goto add_reg; 8553c950e2eSAnatolij Gustschin else 8563c950e2eSAnatolij Gustschin goto err_size; 8573c950e2eSAnatolij Gustschin } else if (ret < 0) 8583c950e2eSAnatolij Gustschin goto err_prop; 8593c950e2eSAnatolij Gustschin 8603c950e2eSAnatolij Gustschin add_label: 8613c950e2eSAnatolij Gustschin ret = fdt_setprop_string(blob, newoff, "label", part->name); 8623c950e2eSAnatolij Gustschin if (ret == -FDT_ERR_NOSPACE) { 8633c950e2eSAnatolij Gustschin ret = fdt_increase_size(blob, 512); 8643c950e2eSAnatolij Gustschin if (!ret) 8653c950e2eSAnatolij Gustschin goto add_label; 8663c950e2eSAnatolij Gustschin else 8673c950e2eSAnatolij Gustschin goto err_size; 8683c950e2eSAnatolij Gustschin } else if (ret < 0) 8693c950e2eSAnatolij Gustschin goto err_prop; 8703c950e2eSAnatolij Gustschin 8713c950e2eSAnatolij Gustschin part_num++; 8723c950e2eSAnatolij Gustschin } 8733c950e2eSAnatolij Gustschin return 0; 8743c950e2eSAnatolij Gustschin err_size: 8753c950e2eSAnatolij Gustschin printf("Can't increase blob size: %s\n", fdt_strerror(ret)); 8763c950e2eSAnatolij Gustschin return ret; 8773c950e2eSAnatolij Gustschin err_prop: 8783c950e2eSAnatolij Gustschin printf("Can't add property: %s\n", fdt_strerror(ret)); 8793c950e2eSAnatolij Gustschin return ret; 8803c950e2eSAnatolij Gustschin } 8813c950e2eSAnatolij Gustschin 8823c950e2eSAnatolij Gustschin /* 8833c950e2eSAnatolij Gustschin * Update partitions in nor/nand nodes using info from 8843c950e2eSAnatolij Gustschin * mtdparts environment variable. The nodes to update are 8853c950e2eSAnatolij Gustschin * specified by node_info structure which contains mtd device 8863c950e2eSAnatolij Gustschin * type and compatible string: E. g. the board code in 8873c950e2eSAnatolij Gustschin * ft_board_setup() could use: 8883c950e2eSAnatolij Gustschin * 8893c950e2eSAnatolij Gustschin * struct node_info nodes[] = { 8903c950e2eSAnatolij Gustschin * { "fsl,mpc5121-nfc", MTD_DEV_TYPE_NAND, }, 8913c950e2eSAnatolij Gustschin * { "cfi-flash", MTD_DEV_TYPE_NOR, }, 8923c950e2eSAnatolij Gustschin * }; 8933c950e2eSAnatolij Gustschin * 8943c950e2eSAnatolij Gustschin * fdt_fixup_mtdparts(blob, nodes, ARRAY_SIZE(nodes)); 8953c950e2eSAnatolij Gustschin */ 8965f4e32d0SMasahiro Yamada void fdt_fixup_mtdparts(void *blob, const struct node_info *node_info, 8975f4e32d0SMasahiro Yamada int node_info_size) 8983c950e2eSAnatolij Gustschin { 8993c950e2eSAnatolij Gustschin struct mtd_device *dev; 9003c950e2eSAnatolij Gustschin int i, idx; 9013c950e2eSAnatolij Gustschin int noff; 9023c950e2eSAnatolij Gustschin 9033c950e2eSAnatolij Gustschin if (mtdparts_init() != 0) 9043c950e2eSAnatolij Gustschin return; 9053c950e2eSAnatolij Gustschin 9063c950e2eSAnatolij Gustschin for (i = 0; i < node_info_size; i++) { 9073c950e2eSAnatolij Gustschin idx = 0; 9085f4e32d0SMasahiro Yamada noff = fdt_node_offset_by_compatible(blob, -1, 9095f4e32d0SMasahiro Yamada node_info[i].compat); 9103c950e2eSAnatolij Gustschin while (noff != -FDT_ERR_NOTFOUND) { 9113c950e2eSAnatolij Gustschin debug("%s: %s, mtd dev type %d\n", 9123c950e2eSAnatolij Gustschin fdt_get_name(blob, noff, 0), 9135f4e32d0SMasahiro Yamada node_info[i].compat, node_info[i].type); 9145f4e32d0SMasahiro Yamada dev = device_find(node_info[i].type, idx++); 9153c950e2eSAnatolij Gustschin if (dev) { 9163c950e2eSAnatolij Gustschin if (fdt_node_set_part_info(blob, noff, dev)) 9173c950e2eSAnatolij Gustschin return; /* return on error */ 9183c950e2eSAnatolij Gustschin } 9193c950e2eSAnatolij Gustschin 9203c950e2eSAnatolij Gustschin /* Jump to next flash node */ 9213c950e2eSAnatolij Gustschin noff = fdt_node_offset_by_compatible(blob, noff, 9225f4e32d0SMasahiro Yamada node_info[i].compat); 9233c950e2eSAnatolij Gustschin } 9243c950e2eSAnatolij Gustschin } 9253c950e2eSAnatolij Gustschin } 9263c950e2eSAnatolij Gustschin #endif 92749b97d9cSKumar Gala 92849b97d9cSKumar Gala void fdt_del_node_and_alias(void *blob, const char *alias) 92949b97d9cSKumar Gala { 93049b97d9cSKumar Gala int off = fdt_path_offset(blob, alias); 93149b97d9cSKumar Gala 93249b97d9cSKumar Gala if (off < 0) 93349b97d9cSKumar Gala return; 93449b97d9cSKumar Gala 93549b97d9cSKumar Gala fdt_del_node(blob, off); 93649b97d9cSKumar Gala 93749b97d9cSKumar Gala off = fdt_path_offset(blob, "/aliases"); 93849b97d9cSKumar Gala fdt_delprop(blob, off, alias); 93949b97d9cSKumar Gala } 940a0342c08SKumar Gala 941a0342c08SKumar Gala /* Max address size we deal with */ 942a0342c08SKumar Gala #define OF_MAX_ADDR_CELLS 4 943a0ae380bSBin Meng #define OF_BAD_ADDR FDT_ADDR_T_NONE 9444428f3c8SPrzemyslaw Marczak #define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \ 9454428f3c8SPrzemyslaw Marczak (ns) > 0) 946a0342c08SKumar Gala 947a0342c08SKumar Gala /* Debug utility */ 948a0342c08SKumar Gala #ifdef DEBUG 9498aa5ec6eSKim Phillips static void of_dump_addr(const char *s, const fdt32_t *addr, int na) 950a0342c08SKumar Gala { 951a0342c08SKumar Gala printf("%s", s); 952a0342c08SKumar Gala while(na--) 953a0342c08SKumar Gala printf(" %08x", *(addr++)); 954a0342c08SKumar Gala printf("\n"); 955a0342c08SKumar Gala } 956a0342c08SKumar Gala #else 9578aa5ec6eSKim Phillips static void of_dump_addr(const char *s, const fdt32_t *addr, int na) { } 958a0342c08SKumar Gala #endif 959a0342c08SKumar Gala 9600a222d53SPaul Burton /** 9610a222d53SPaul Burton * struct of_bus - Callbacks for bus specific translators 96249717b18SPaul Burton * @name: A string used to identify this bus in debug output. 96349717b18SPaul Burton * @addresses: The name of the DT property from which addresses are 96449717b18SPaul Burton * to be read, typically "reg". 9650a222d53SPaul Burton * @match: Return non-zero if the node whose parent is at 9660a222d53SPaul Burton * parentoffset in the FDT blob corresponds to a bus 9670a222d53SPaul Burton * of this type, otherwise return zero. If NULL a match 9680a222d53SPaul Burton * is assumed. 96949717b18SPaul Burton * @count_cells:Count how many cells (be32 values) a node whose parent 97049717b18SPaul Burton * is at parentoffset in the FDT blob will require to 97149717b18SPaul Burton * represent its address (written to *addrc) & size 97249717b18SPaul Burton * (written to *sizec). 97349717b18SPaul Burton * @map: Map the address addr from the address space of this 97449717b18SPaul Burton * bus to that of its parent, making use of the ranges 97549717b18SPaul Burton * read from DT to an array at range. na and ns are the 97649717b18SPaul Burton * number of cells (be32 values) used to hold and address 97749717b18SPaul Burton * or size, respectively, for this bus. pna is the number 97849717b18SPaul Burton * of cells used to hold an address for the parent bus. 97949717b18SPaul Burton * Returns the address in the address space of the parent 98049717b18SPaul Burton * bus. 98149717b18SPaul Burton * @translate: Update the value of the address cells at addr within an 98249717b18SPaul Burton * FDT by adding offset to it. na specifies the number of 98349717b18SPaul Burton * cells used to hold the address being translated. Returns 98449717b18SPaul Burton * zero on success, non-zero on error. 9850a222d53SPaul Burton * 9860a222d53SPaul Burton * Each bus type will include a struct of_bus in the of_busses array, 9870a222d53SPaul Burton * providing implementations of some or all of the functions used to 9880a222d53SPaul Burton * match the bus & handle address translation for its children. 9890a222d53SPaul Burton */ 990a0342c08SKumar Gala struct of_bus { 991a0342c08SKumar Gala const char *name; 992a0342c08SKumar Gala const char *addresses; 99311e44fc6SStephen Warren int (*match)(const void *blob, int parentoffset); 99411e44fc6SStephen Warren void (*count_cells)(const void *blob, int parentoffset, 995a0342c08SKumar Gala int *addrc, int *sizec); 9968aa5ec6eSKim Phillips u64 (*map)(fdt32_t *addr, const fdt32_t *range, 997a0342c08SKumar Gala int na, int ns, int pna); 9988aa5ec6eSKim Phillips int (*translate)(fdt32_t *addr, u64 offset, int na); 999a0342c08SKumar Gala }; 1000a0342c08SKumar Gala 1001a0342c08SKumar Gala /* Default translator (generic bus) */ 1002eed36609SSimon Glass void fdt_support_default_count_cells(const void *blob, int parentoffset, 1003a0342c08SKumar Gala int *addrc, int *sizec) 1004a0342c08SKumar Gala { 10058aa5ec6eSKim Phillips const fdt32_t *prop; 10066395f318SScott Wood 1007933cdbb4SSimon Glass if (addrc) 1008933cdbb4SSimon Glass *addrc = fdt_address_cells(blob, parentoffset); 10096395f318SScott Wood 10106395f318SScott Wood if (sizec) { 10116395f318SScott Wood prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL); 10126395f318SScott Wood if (prop) 10138aa5ec6eSKim Phillips *sizec = be32_to_cpup(prop); 10146395f318SScott Wood else 10156395f318SScott Wood *sizec = 1; 10166395f318SScott Wood } 1017a0342c08SKumar Gala } 1018a0342c08SKumar Gala 10198aa5ec6eSKim Phillips static u64 of_bus_default_map(fdt32_t *addr, const fdt32_t *range, 1020a0342c08SKumar Gala int na, int ns, int pna) 1021a0342c08SKumar Gala { 1022a0342c08SKumar Gala u64 cp, s, da; 1023a0342c08SKumar Gala 1024eed36609SSimon Glass cp = fdt_read_number(range, na); 1025eed36609SSimon Glass s = fdt_read_number(range + na + pna, ns); 1026eed36609SSimon Glass da = fdt_read_number(addr, na); 1027a0342c08SKumar Gala 1028e48f3741SSimon Glass debug("OF: default map, cp=%" PRIu64 ", s=%" PRIu64 1029e48f3741SSimon Glass ", da=%" PRIu64 "\n", cp, s, da); 1030a0342c08SKumar Gala 1031a0342c08SKumar Gala if (da < cp || da >= (cp + s)) 1032a0342c08SKumar Gala return OF_BAD_ADDR; 1033a0342c08SKumar Gala return da - cp; 1034a0342c08SKumar Gala } 1035a0342c08SKumar Gala 10368aa5ec6eSKim Phillips static int of_bus_default_translate(fdt32_t *addr, u64 offset, int na) 1037a0342c08SKumar Gala { 1038eed36609SSimon Glass u64 a = fdt_read_number(addr, na); 1039a0342c08SKumar Gala memset(addr, 0, na * 4); 1040a0342c08SKumar Gala a += offset; 1041a0342c08SKumar Gala if (na > 1) 10428aa5ec6eSKim Phillips addr[na - 2] = cpu_to_fdt32(a >> 32); 10438aa5ec6eSKim Phillips addr[na - 1] = cpu_to_fdt32(a & 0xffffffffu); 1044a0342c08SKumar Gala 1045a0342c08SKumar Gala return 0; 1046a0342c08SKumar Gala } 1047a0342c08SKumar Gala 10480a222d53SPaul Burton #ifdef CONFIG_OF_ISA_BUS 10490a222d53SPaul Burton 10500a222d53SPaul Burton /* ISA bus translator */ 105111e44fc6SStephen Warren static int of_bus_isa_match(const void *blob, int parentoffset) 10520a222d53SPaul Burton { 10530a222d53SPaul Burton const char *name; 10540a222d53SPaul Burton 10550a222d53SPaul Burton name = fdt_get_name(blob, parentoffset, NULL); 10560a222d53SPaul Burton if (!name) 10570a222d53SPaul Burton return 0; 10580a222d53SPaul Burton 10590a222d53SPaul Burton return !strcmp(name, "isa"); 10600a222d53SPaul Burton } 10610a222d53SPaul Burton 106211e44fc6SStephen Warren static void of_bus_isa_count_cells(const void *blob, int parentoffset, 10630a222d53SPaul Burton int *addrc, int *sizec) 10640a222d53SPaul Burton { 10650a222d53SPaul Burton if (addrc) 10660a222d53SPaul Burton *addrc = 2; 10670a222d53SPaul Burton if (sizec) 10680a222d53SPaul Burton *sizec = 1; 10690a222d53SPaul Burton } 10700a222d53SPaul Burton 10710a222d53SPaul Burton static u64 of_bus_isa_map(fdt32_t *addr, const fdt32_t *range, 10720a222d53SPaul Burton int na, int ns, int pna) 10730a222d53SPaul Burton { 10740a222d53SPaul Burton u64 cp, s, da; 10750a222d53SPaul Burton 10760a222d53SPaul Burton /* Check address type match */ 10770a222d53SPaul Burton if ((addr[0] ^ range[0]) & cpu_to_be32(1)) 10780a222d53SPaul Burton return OF_BAD_ADDR; 10790a222d53SPaul Burton 1080eed36609SSimon Glass cp = fdt_read_number(range + 1, na - 1); 1081eed36609SSimon Glass s = fdt_read_number(range + na + pna, ns); 1082eed36609SSimon Glass da = fdt_read_number(addr + 1, na - 1); 10830a222d53SPaul Burton 10840a222d53SPaul Burton debug("OF: ISA map, cp=%" PRIu64 ", s=%" PRIu64 10850a222d53SPaul Burton ", da=%" PRIu64 "\n", cp, s, da); 10860a222d53SPaul Burton 10870a222d53SPaul Burton if (da < cp || da >= (cp + s)) 10880a222d53SPaul Burton return OF_BAD_ADDR; 10890a222d53SPaul Burton return da - cp; 10900a222d53SPaul Burton } 10910a222d53SPaul Burton 10920a222d53SPaul Burton static int of_bus_isa_translate(fdt32_t *addr, u64 offset, int na) 10930a222d53SPaul Burton { 10940a222d53SPaul Burton return of_bus_default_translate(addr + 1, offset, na - 1); 10950a222d53SPaul Burton } 10960a222d53SPaul Burton 10970a222d53SPaul Burton #endif /* CONFIG_OF_ISA_BUS */ 10980a222d53SPaul Burton 1099a0342c08SKumar Gala /* Array of bus specific translators */ 1100a0342c08SKumar Gala static struct of_bus of_busses[] = { 11010a222d53SPaul Burton #ifdef CONFIG_OF_ISA_BUS 11020a222d53SPaul Burton /* ISA */ 11030a222d53SPaul Burton { 11040a222d53SPaul Burton .name = "isa", 11050a222d53SPaul Burton .addresses = "reg", 11060a222d53SPaul Burton .match = of_bus_isa_match, 11070a222d53SPaul Burton .count_cells = of_bus_isa_count_cells, 11080a222d53SPaul Burton .map = of_bus_isa_map, 11090a222d53SPaul Burton .translate = of_bus_isa_translate, 11100a222d53SPaul Burton }, 11110a222d53SPaul Burton #endif /* CONFIG_OF_ISA_BUS */ 1112a0342c08SKumar Gala /* Default */ 1113a0342c08SKumar Gala { 1114a0342c08SKumar Gala .name = "default", 1115a0342c08SKumar Gala .addresses = "reg", 1116eed36609SSimon Glass .count_cells = fdt_support_default_count_cells, 1117a0342c08SKumar Gala .map = of_bus_default_map, 1118a0342c08SKumar Gala .translate = of_bus_default_translate, 1119a0342c08SKumar Gala }, 1120a0342c08SKumar Gala }; 1121a0342c08SKumar Gala 112211e44fc6SStephen Warren static struct of_bus *of_match_bus(const void *blob, int parentoffset) 11230a222d53SPaul Burton { 11240a222d53SPaul Burton struct of_bus *bus; 11250a222d53SPaul Burton 11260a222d53SPaul Burton if (ARRAY_SIZE(of_busses) == 1) 11270a222d53SPaul Burton return of_busses; 11280a222d53SPaul Burton 11290a222d53SPaul Burton for (bus = of_busses; bus; bus++) { 11300a222d53SPaul Burton if (!bus->match || bus->match(blob, parentoffset)) 11310a222d53SPaul Burton return bus; 11320a222d53SPaul Burton } 11330a222d53SPaul Burton 11340a222d53SPaul Burton /* 11350a222d53SPaul Burton * We should always have matched the default bus at least, since 11360a222d53SPaul Burton * it has a NULL match field. If we didn't then it somehow isn't 11370a222d53SPaul Burton * in the of_busses array or something equally catastrophic has 11380a222d53SPaul Burton * gone wrong. 11390a222d53SPaul Burton */ 11400a222d53SPaul Burton assert(0); 11410a222d53SPaul Burton return NULL; 11420a222d53SPaul Burton } 11430a222d53SPaul Burton 114411e44fc6SStephen Warren static int of_translate_one(const void *blob, int parent, struct of_bus *bus, 11458aa5ec6eSKim Phillips struct of_bus *pbus, fdt32_t *addr, 1146a0342c08SKumar Gala int na, int ns, int pna, const char *rprop) 1147a0342c08SKumar Gala { 11488aa5ec6eSKim Phillips const fdt32_t *ranges; 1149a0342c08SKumar Gala int rlen; 1150a0342c08SKumar Gala int rone; 1151a0342c08SKumar Gala u64 offset = OF_BAD_ADDR; 1152a0342c08SKumar Gala 1153a0342c08SKumar Gala /* Normally, an absence of a "ranges" property means we are 1154a0342c08SKumar Gala * crossing a non-translatable boundary, and thus the addresses 1155a0342c08SKumar Gala * below the current not cannot be converted to CPU physical ones. 1156a0342c08SKumar Gala * Unfortunately, while this is very clear in the spec, it's not 1157a0342c08SKumar Gala * what Apple understood, and they do have things like /uni-n or 1158a0342c08SKumar Gala * /ht nodes with no "ranges" property and a lot of perfectly 1159a0342c08SKumar Gala * useable mapped devices below them. Thus we treat the absence of 1160a0342c08SKumar Gala * "ranges" as equivalent to an empty "ranges" property which means 1161a0342c08SKumar Gala * a 1:1 translation at that level. It's up to the caller not to try 1162a0342c08SKumar Gala * to translate addresses that aren't supposed to be translated in 1163a0342c08SKumar Gala * the first place. --BenH. 1164a0342c08SKumar Gala */ 11658aa5ec6eSKim Phillips ranges = fdt_getprop(blob, parent, rprop, &rlen); 1166a0342c08SKumar Gala if (ranges == NULL || rlen == 0) { 1167eed36609SSimon Glass offset = fdt_read_number(addr, na); 1168a0342c08SKumar Gala memset(addr, 0, pna * 4); 1169a0342c08SKumar Gala debug("OF: no ranges, 1:1 translation\n"); 1170a0342c08SKumar Gala goto finish; 1171a0342c08SKumar Gala } 1172a0342c08SKumar Gala 1173a0342c08SKumar Gala debug("OF: walking ranges...\n"); 1174a0342c08SKumar Gala 1175a0342c08SKumar Gala /* Now walk through the ranges */ 1176a0342c08SKumar Gala rlen /= 4; 1177a0342c08SKumar Gala rone = na + pna + ns; 1178a0342c08SKumar Gala for (; rlen >= rone; rlen -= rone, ranges += rone) { 1179a0342c08SKumar Gala offset = bus->map(addr, ranges, na, ns, pna); 1180a0342c08SKumar Gala if (offset != OF_BAD_ADDR) 1181a0342c08SKumar Gala break; 1182a0342c08SKumar Gala } 1183a0342c08SKumar Gala if (offset == OF_BAD_ADDR) { 1184a0342c08SKumar Gala debug("OF: not found !\n"); 1185a0342c08SKumar Gala return 1; 1186a0342c08SKumar Gala } 1187a0342c08SKumar Gala memcpy(addr, ranges + na, 4 * pna); 1188a0342c08SKumar Gala 1189a0342c08SKumar Gala finish: 1190a0342c08SKumar Gala of_dump_addr("OF: parent translation for:", addr, pna); 1191e48f3741SSimon Glass debug("OF: with offset: %" PRIu64 "\n", offset); 1192a0342c08SKumar Gala 1193a0342c08SKumar Gala /* Translate it into parent bus space */ 1194a0342c08SKumar Gala return pbus->translate(addr, offset, pna); 1195a0342c08SKumar Gala } 1196a0342c08SKumar Gala 1197a0342c08SKumar Gala /* 1198a0342c08SKumar Gala * Translate an address from the device-tree into a CPU physical address, 1199a0342c08SKumar Gala * this walks up the tree and applies the various bus mappings on the 1200a0342c08SKumar Gala * way. 1201a0342c08SKumar Gala * 1202a0342c08SKumar Gala * Note: We consider that crossing any level with #size-cells == 0 to mean 1203a0342c08SKumar Gala * that translation is impossible (that is we are not dealing with a value 1204a0342c08SKumar Gala * that can be mapped to a cpu physical address). This is not really specified 1205a0342c08SKumar Gala * that way, but this is traditionally the way IBM at least do things 1206a0342c08SKumar Gala */ 120711e44fc6SStephen Warren static u64 __of_translate_address(const void *blob, int node_offset, 120811e44fc6SStephen Warren const fdt32_t *in_addr, const char *rprop) 1209a0342c08SKumar Gala { 1210a0342c08SKumar Gala int parent; 1211a0342c08SKumar Gala struct of_bus *bus, *pbus; 12128aa5ec6eSKim Phillips fdt32_t addr[OF_MAX_ADDR_CELLS]; 1213a0342c08SKumar Gala int na, ns, pna, pns; 1214a0342c08SKumar Gala u64 result = OF_BAD_ADDR; 1215a0342c08SKumar Gala 1216a0342c08SKumar Gala debug("OF: ** translation for device %s **\n", 1217a0342c08SKumar Gala fdt_get_name(blob, node_offset, NULL)); 1218a0342c08SKumar Gala 1219a0342c08SKumar Gala /* Get parent & match bus type */ 1220a0342c08SKumar Gala parent = fdt_parent_offset(blob, node_offset); 1221a0342c08SKumar Gala if (parent < 0) 1222a0342c08SKumar Gala goto bail; 12230a222d53SPaul Burton bus = of_match_bus(blob, parent); 1224a0342c08SKumar Gala 1225a0342c08SKumar Gala /* Cound address cells & copy address locally */ 12266395f318SScott Wood bus->count_cells(blob, parent, &na, &ns); 12274428f3c8SPrzemyslaw Marczak if (!OF_CHECK_COUNTS(na, ns)) { 1228a0342c08SKumar Gala printf("%s: Bad cell count for %s\n", __FUNCTION__, 1229a0342c08SKumar Gala fdt_get_name(blob, node_offset, NULL)); 1230a0342c08SKumar Gala goto bail; 1231a0342c08SKumar Gala } 1232a0342c08SKumar Gala memcpy(addr, in_addr, na * 4); 1233a0342c08SKumar Gala 1234a0342c08SKumar Gala debug("OF: bus is %s (na=%d, ns=%d) on %s\n", 1235a0342c08SKumar Gala bus->name, na, ns, fdt_get_name(blob, parent, NULL)); 1236a0342c08SKumar Gala of_dump_addr("OF: translating address:", addr, na); 1237a0342c08SKumar Gala 1238a0342c08SKumar Gala /* Translate */ 1239a0342c08SKumar Gala for (;;) { 1240a0342c08SKumar Gala /* Switch to parent bus */ 1241a0342c08SKumar Gala node_offset = parent; 1242a0342c08SKumar Gala parent = fdt_parent_offset(blob, node_offset); 1243a0342c08SKumar Gala 1244a0342c08SKumar Gala /* If root, we have finished */ 1245a0342c08SKumar Gala if (parent < 0) { 1246a0342c08SKumar Gala debug("OF: reached root node\n"); 1247eed36609SSimon Glass result = fdt_read_number(addr, na); 1248a0342c08SKumar Gala break; 1249a0342c08SKumar Gala } 1250a0342c08SKumar Gala 1251a0342c08SKumar Gala /* Get new parent bus and counts */ 12520a222d53SPaul Burton pbus = of_match_bus(blob, parent); 12536395f318SScott Wood pbus->count_cells(blob, parent, &pna, &pns); 12544428f3c8SPrzemyslaw Marczak if (!OF_CHECK_COUNTS(pna, pns)) { 1255a0342c08SKumar Gala printf("%s: Bad cell count for %s\n", __FUNCTION__, 1256a0342c08SKumar Gala fdt_get_name(blob, node_offset, NULL)); 1257a0342c08SKumar Gala break; 1258a0342c08SKumar Gala } 1259a0342c08SKumar Gala 1260a0342c08SKumar Gala debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n", 1261a0342c08SKumar Gala pbus->name, pna, pns, fdt_get_name(blob, parent, NULL)); 1262a0342c08SKumar Gala 1263a0342c08SKumar Gala /* Apply bus translation */ 1264a0342c08SKumar Gala if (of_translate_one(blob, node_offset, bus, pbus, 1265a0342c08SKumar Gala addr, na, ns, pna, rprop)) 1266a0342c08SKumar Gala break; 1267a0342c08SKumar Gala 1268a0342c08SKumar Gala /* Complete the move up one level */ 1269a0342c08SKumar Gala na = pna; 1270a0342c08SKumar Gala ns = pns; 1271a0342c08SKumar Gala bus = pbus; 1272a0342c08SKumar Gala 1273a0342c08SKumar Gala of_dump_addr("OF: one level translation:", addr, na); 1274a0342c08SKumar Gala } 1275a0342c08SKumar Gala bail: 1276a0342c08SKumar Gala 1277a0342c08SKumar Gala return result; 1278a0342c08SKumar Gala } 1279a0342c08SKumar Gala 128011e44fc6SStephen Warren u64 fdt_translate_address(const void *blob, int node_offset, 128111e44fc6SStephen Warren const fdt32_t *in_addr) 1282a0342c08SKumar Gala { 1283a0342c08SKumar Gala return __of_translate_address(blob, node_offset, in_addr, "ranges"); 1284a0342c08SKumar Gala } 128575e73afdSKumar Gala 128675e73afdSKumar Gala /** 128775e73afdSKumar Gala * fdt_node_offset_by_compat_reg: Find a node that matches compatiable and 128875e73afdSKumar Gala * who's reg property matches a physical cpu address 128975e73afdSKumar Gala * 129075e73afdSKumar Gala * @blob: ptr to device tree 129175e73afdSKumar Gala * @compat: compatiable string to match 129275e73afdSKumar Gala * @compat_off: property name 129375e73afdSKumar Gala * 129475e73afdSKumar Gala */ 129575e73afdSKumar Gala int fdt_node_offset_by_compat_reg(void *blob, const char *compat, 129675e73afdSKumar Gala phys_addr_t compat_off) 129775e73afdSKumar Gala { 129875e73afdSKumar Gala int len, off = fdt_node_offset_by_compatible(blob, -1, compat); 129975e73afdSKumar Gala while (off != -FDT_ERR_NOTFOUND) { 13008aa5ec6eSKim Phillips const fdt32_t *reg = fdt_getprop(blob, off, "reg", &len); 130175e73afdSKumar Gala if (reg) { 130275e73afdSKumar Gala if (compat_off == fdt_translate_address(blob, off, reg)) 130375e73afdSKumar Gala return off; 130475e73afdSKumar Gala } 130575e73afdSKumar Gala off = fdt_node_offset_by_compatible(blob, off, compat); 130675e73afdSKumar Gala } 130775e73afdSKumar Gala 130875e73afdSKumar Gala return -FDT_ERR_NOTFOUND; 130975e73afdSKumar Gala } 131075e73afdSKumar Gala 1311b4b847e9SKumar Gala /** 1312b4b847e9SKumar Gala * fdt_alloc_phandle: Return next free phandle value 1313b4b847e9SKumar Gala * 1314b4b847e9SKumar Gala * @blob: ptr to device tree 1315b4b847e9SKumar Gala */ 1316b4b847e9SKumar Gala int fdt_alloc_phandle(void *blob) 1317b4b847e9SKumar Gala { 1318b4141195SMasahiro Yamada int offset; 1319b4141195SMasahiro Yamada uint32_t phandle = 0; 132075e73afdSKumar Gala 1321b4b847e9SKumar Gala for (offset = fdt_next_node(blob, -1, NULL); offset >= 0; 1322b4b847e9SKumar Gala offset = fdt_next_node(blob, offset, NULL)) { 132350bf17bdSTimur Tabi phandle = max(phandle, fdt_get_phandle(blob, offset)); 1324b4b847e9SKumar Gala } 1325b4b847e9SKumar Gala 1326b4b847e9SKumar Gala return phandle + 1; 1327b4b847e9SKumar Gala } 1328beca5a5fSAnatolij Gustschin 1329a8d2a75dSGerald Van Baren /* 1330f117c0f0SKumar Gala * fdt_set_phandle: Create a phandle property for the given node 1331a8d2a75dSGerald Van Baren * 1332a8d2a75dSGerald Van Baren * @fdt: ptr to device tree 1333a8d2a75dSGerald Van Baren * @nodeoffset: node to update 1334a8d2a75dSGerald Van Baren * @phandle: phandle value to set (must be unique) 1335a8d2a75dSGerald Van Baren */ 1336f117c0f0SKumar Gala int fdt_set_phandle(void *fdt, int nodeoffset, uint32_t phandle) 1337a8d2a75dSGerald Van Baren { 1338a8d2a75dSGerald Van Baren int ret; 1339a8d2a75dSGerald Van Baren 1340a8d2a75dSGerald Van Baren #ifdef DEBUG 1341a8d2a75dSGerald Van Baren int off = fdt_node_offset_by_phandle(fdt, phandle); 1342a8d2a75dSGerald Van Baren 1343a8d2a75dSGerald Van Baren if ((off >= 0) && (off != nodeoffset)) { 1344a8d2a75dSGerald Van Baren char buf[64]; 1345a8d2a75dSGerald Van Baren 1346a8d2a75dSGerald Van Baren fdt_get_path(fdt, nodeoffset, buf, sizeof(buf)); 1347a8d2a75dSGerald Van Baren printf("Trying to update node %s with phandle %u ", 1348a8d2a75dSGerald Van Baren buf, phandle); 1349a8d2a75dSGerald Van Baren 1350a8d2a75dSGerald Van Baren fdt_get_path(fdt, off, buf, sizeof(buf)); 1351a8d2a75dSGerald Van Baren printf("that already exists in node %s.\n", buf); 1352a8d2a75dSGerald Van Baren return -FDT_ERR_BADPHANDLE; 1353a8d2a75dSGerald Van Baren } 1354a8d2a75dSGerald Van Baren #endif 1355a8d2a75dSGerald Van Baren 1356a8d2a75dSGerald Van Baren ret = fdt_setprop_cell(fdt, nodeoffset, "phandle", phandle); 1357a8d2a75dSGerald Van Baren if (ret < 0) 1358a8d2a75dSGerald Van Baren return ret; 1359a8d2a75dSGerald Van Baren 1360a8d2a75dSGerald Van Baren /* 1361a8d2a75dSGerald Van Baren * For now, also set the deprecated "linux,phandle" property, so that we 1362a8d2a75dSGerald Van Baren * don't break older kernels. 1363a8d2a75dSGerald Van Baren */ 1364a8d2a75dSGerald Van Baren ret = fdt_setprop_cell(fdt, nodeoffset, "linux,phandle", phandle); 1365a8d2a75dSGerald Van Baren 1366a8d2a75dSGerald Van Baren return ret; 1367a8d2a75dSGerald Van Baren } 1368a8d2a75dSGerald Van Baren 136910aeabd1SKumar Gala /* 137010aeabd1SKumar Gala * fdt_create_phandle: Create a phandle property for the given node 137110aeabd1SKumar Gala * 137210aeabd1SKumar Gala * @fdt: ptr to device tree 137310aeabd1SKumar Gala * @nodeoffset: node to update 137410aeabd1SKumar Gala */ 13753c927cccSTimur Tabi unsigned int fdt_create_phandle(void *fdt, int nodeoffset) 137610aeabd1SKumar Gala { 137710aeabd1SKumar Gala /* see if there is a phandle already */ 137810aeabd1SKumar Gala int phandle = fdt_get_phandle(fdt, nodeoffset); 137910aeabd1SKumar Gala 138010aeabd1SKumar Gala /* if we got 0, means no phandle so create one */ 138110aeabd1SKumar Gala if (phandle == 0) { 13823c927cccSTimur Tabi int ret; 13833c927cccSTimur Tabi 138410aeabd1SKumar Gala phandle = fdt_alloc_phandle(fdt); 13853c927cccSTimur Tabi ret = fdt_set_phandle(fdt, nodeoffset, phandle); 13863c927cccSTimur Tabi if (ret < 0) { 13873c927cccSTimur Tabi printf("Can't set phandle %u: %s\n", phandle, 13883c927cccSTimur Tabi fdt_strerror(ret)); 13893c927cccSTimur Tabi return 0; 13903c927cccSTimur Tabi } 139110aeabd1SKumar Gala } 139210aeabd1SKumar Gala 139310aeabd1SKumar Gala return phandle; 139410aeabd1SKumar Gala } 139510aeabd1SKumar Gala 13962a523f52SShengzhou Liu /* 13972a523f52SShengzhou Liu * fdt_set_node_status: Set status for the given node 13982a523f52SShengzhou Liu * 13992a523f52SShengzhou Liu * @fdt: ptr to device tree 14002a523f52SShengzhou Liu * @nodeoffset: node to update 14012a523f52SShengzhou Liu * @status: FDT_STATUS_OKAY, FDT_STATUS_DISABLED, 14022a523f52SShengzhou Liu * FDT_STATUS_FAIL, FDT_STATUS_FAIL_ERROR_CODE 14032a523f52SShengzhou Liu * @error_code: optional, only used if status is FDT_STATUS_FAIL_ERROR_CODE 14042a523f52SShengzhou Liu */ 14052a523f52SShengzhou Liu int fdt_set_node_status(void *fdt, int nodeoffset, 14062a523f52SShengzhou Liu enum fdt_status status, unsigned int error_code) 14072a523f52SShengzhou Liu { 14082a523f52SShengzhou Liu char buf[16]; 14092a523f52SShengzhou Liu int ret = 0; 14102a523f52SShengzhou Liu 14112a523f52SShengzhou Liu if (nodeoffset < 0) 14122a523f52SShengzhou Liu return nodeoffset; 14132a523f52SShengzhou Liu 14142a523f52SShengzhou Liu switch (status) { 14152a523f52SShengzhou Liu case FDT_STATUS_OKAY: 14162a523f52SShengzhou Liu ret = fdt_setprop_string(fdt, nodeoffset, "status", "okay"); 14172a523f52SShengzhou Liu break; 14182a523f52SShengzhou Liu case FDT_STATUS_DISABLED: 14192a523f52SShengzhou Liu ret = fdt_setprop_string(fdt, nodeoffset, "status", "disabled"); 14202a523f52SShengzhou Liu break; 14212a523f52SShengzhou Liu case FDT_STATUS_FAIL: 14222a523f52SShengzhou Liu ret = fdt_setprop_string(fdt, nodeoffset, "status", "fail"); 14232a523f52SShengzhou Liu break; 14242a523f52SShengzhou Liu case FDT_STATUS_FAIL_ERROR_CODE: 14252a523f52SShengzhou Liu sprintf(buf, "fail-%d", error_code); 14262a523f52SShengzhou Liu ret = fdt_setprop_string(fdt, nodeoffset, "status", buf); 14272a523f52SShengzhou Liu break; 14282a523f52SShengzhou Liu default: 14292a523f52SShengzhou Liu printf("Invalid fdt status: %x\n", status); 14302a523f52SShengzhou Liu ret = -1; 14312a523f52SShengzhou Liu break; 14322a523f52SShengzhou Liu } 14332a523f52SShengzhou Liu 14342a523f52SShengzhou Liu return ret; 14352a523f52SShengzhou Liu } 14362a523f52SShengzhou Liu 14372a523f52SShengzhou Liu /* 14382a523f52SShengzhou Liu * fdt_set_status_by_alias: Set status for the given node given an alias 14392a523f52SShengzhou Liu * 14402a523f52SShengzhou Liu * @fdt: ptr to device tree 14412a523f52SShengzhou Liu * @alias: alias of node to update 14422a523f52SShengzhou Liu * @status: FDT_STATUS_OKAY, FDT_STATUS_DISABLED, 14432a523f52SShengzhou Liu * FDT_STATUS_FAIL, FDT_STATUS_FAIL_ERROR_CODE 14442a523f52SShengzhou Liu * @error_code: optional, only used if status is FDT_STATUS_FAIL_ERROR_CODE 14452a523f52SShengzhou Liu */ 14462a523f52SShengzhou Liu int fdt_set_status_by_alias(void *fdt, const char* alias, 14472a523f52SShengzhou Liu enum fdt_status status, unsigned int error_code) 14482a523f52SShengzhou Liu { 14492a523f52SShengzhou Liu int offset = fdt_path_offset(fdt, alias); 14502a523f52SShengzhou Liu 14512a523f52SShengzhou Liu return fdt_set_node_status(fdt, offset, status, error_code); 14522a523f52SShengzhou Liu } 14532a523f52SShengzhou Liu 1454096eb3f5STom Wai-Hong Tam #if defined(CONFIG_VIDEO) || defined(CONFIG_LCD) 1455beca5a5fSAnatolij Gustschin int fdt_add_edid(void *blob, const char *compat, unsigned char *edid_buf) 1456beca5a5fSAnatolij Gustschin { 1457beca5a5fSAnatolij Gustschin int noff; 1458beca5a5fSAnatolij Gustschin int ret; 1459beca5a5fSAnatolij Gustschin 1460beca5a5fSAnatolij Gustschin noff = fdt_node_offset_by_compatible(blob, -1, compat); 1461beca5a5fSAnatolij Gustschin if (noff != -FDT_ERR_NOTFOUND) { 1462beca5a5fSAnatolij Gustschin debug("%s: %s\n", fdt_get_name(blob, noff, 0), compat); 1463beca5a5fSAnatolij Gustschin add_edid: 1464beca5a5fSAnatolij Gustschin ret = fdt_setprop(blob, noff, "edid", edid_buf, 128); 1465beca5a5fSAnatolij Gustschin if (ret == -FDT_ERR_NOSPACE) { 1466beca5a5fSAnatolij Gustschin ret = fdt_increase_size(blob, 512); 1467beca5a5fSAnatolij Gustschin if (!ret) 1468beca5a5fSAnatolij Gustschin goto add_edid; 1469beca5a5fSAnatolij Gustschin else 1470beca5a5fSAnatolij Gustschin goto err_size; 1471beca5a5fSAnatolij Gustschin } else if (ret < 0) { 1472beca5a5fSAnatolij Gustschin printf("Can't add property: %s\n", fdt_strerror(ret)); 1473beca5a5fSAnatolij Gustschin return ret; 1474beca5a5fSAnatolij Gustschin } 1475beca5a5fSAnatolij Gustschin } 1476beca5a5fSAnatolij Gustschin return 0; 1477beca5a5fSAnatolij Gustschin err_size: 1478beca5a5fSAnatolij Gustschin printf("Can't increase blob size: %s\n", fdt_strerror(ret)); 1479beca5a5fSAnatolij Gustschin return ret; 1480beca5a5fSAnatolij Gustschin } 1481beca5a5fSAnatolij Gustschin #endif 1482bb682001STimur Tabi 1483bb682001STimur Tabi /* 1484bb682001STimur Tabi * Verify the physical address of device tree node for a given alias 1485bb682001STimur Tabi * 1486bb682001STimur Tabi * This function locates the device tree node of a given alias, and then 1487bb682001STimur Tabi * verifies that the physical address of that device matches the given 1488bb682001STimur Tabi * parameter. It displays a message if there is a mismatch. 1489bb682001STimur Tabi * 1490bb682001STimur Tabi * Returns 1 on success, 0 on failure 1491bb682001STimur Tabi */ 1492bb682001STimur Tabi int fdt_verify_alias_address(void *fdt, int anode, const char *alias, u64 addr) 1493bb682001STimur Tabi { 1494bb682001STimur Tabi const char *path; 14958aa5ec6eSKim Phillips const fdt32_t *reg; 1496bb682001STimur Tabi int node, len; 1497bb682001STimur Tabi u64 dt_addr; 1498bb682001STimur Tabi 1499bb682001STimur Tabi path = fdt_getprop(fdt, anode, alias, NULL); 1500bb682001STimur Tabi if (!path) { 1501bb682001STimur Tabi /* If there's no such alias, then it's not a failure */ 1502bb682001STimur Tabi return 1; 1503bb682001STimur Tabi } 1504bb682001STimur Tabi 1505bb682001STimur Tabi node = fdt_path_offset(fdt, path); 1506bb682001STimur Tabi if (node < 0) { 1507bb682001STimur Tabi printf("Warning: device tree alias '%s' points to invalid " 1508bb682001STimur Tabi "node %s.\n", alias, path); 1509bb682001STimur Tabi return 0; 1510bb682001STimur Tabi } 1511bb682001STimur Tabi 1512bb682001STimur Tabi reg = fdt_getprop(fdt, node, "reg", &len); 1513bb682001STimur Tabi if (!reg) { 1514bb682001STimur Tabi printf("Warning: device tree node '%s' has no address.\n", 1515bb682001STimur Tabi path); 1516bb682001STimur Tabi return 0; 1517bb682001STimur Tabi } 1518bb682001STimur Tabi 1519bb682001STimur Tabi dt_addr = fdt_translate_address(fdt, node, reg); 1520bb682001STimur Tabi if (addr != dt_addr) { 1521e48f3741SSimon Glass printf("Warning: U-Boot configured device %s at address %" 1522e48f3741SSimon Glass PRIx64 ",\n but the device tree has it address %" 1523e48f3741SSimon Glass PRIx64 ".\n", alias, addr, dt_addr); 1524bb682001STimur Tabi return 0; 1525bb682001STimur Tabi } 1526bb682001STimur Tabi 1527bb682001STimur Tabi return 1; 1528bb682001STimur Tabi } 1529bb682001STimur Tabi 1530bb682001STimur Tabi /* 1531bb682001STimur Tabi * Returns the base address of an SOC or PCI node 1532bb682001STimur Tabi */ 1533ec002119SSimon Glass u64 fdt_get_base_address(const void *fdt, int node) 1534bb682001STimur Tabi { 1535bb682001STimur Tabi int size; 15368aa5ec6eSKim Phillips const fdt32_t *prop; 1537bb682001STimur Tabi 1538336a4487SSimon Glass prop = fdt_getprop(fdt, node, "reg", &size); 1539bb682001STimur Tabi 1540336a4487SSimon Glass return prop ? fdt_translate_address(fdt, node, prop) : 0; 1541bb682001STimur Tabi } 1542c48e6868SAlexander Graf 1543c48e6868SAlexander Graf /* 1544c48e6868SAlexander Graf * Read a property of size <prop_len>. Currently only supports 1 or 2 cells. 1545c48e6868SAlexander Graf */ 1546c48e6868SAlexander Graf static int fdt_read_prop(const fdt32_t *prop, int prop_len, int cell_off, 1547c48e6868SAlexander Graf uint64_t *val, int cells) 1548c48e6868SAlexander Graf { 1549c48e6868SAlexander Graf const fdt32_t *prop32 = &prop[cell_off]; 1550c48e6868SAlexander Graf const fdt64_t *prop64 = (const fdt64_t *)&prop[cell_off]; 1551c48e6868SAlexander Graf 1552c48e6868SAlexander Graf if ((cell_off + cells) > prop_len) 1553c48e6868SAlexander Graf return -FDT_ERR_NOSPACE; 1554c48e6868SAlexander Graf 1555c48e6868SAlexander Graf switch (cells) { 1556c48e6868SAlexander Graf case 1: 1557c48e6868SAlexander Graf *val = fdt32_to_cpu(*prop32); 1558c48e6868SAlexander Graf break; 1559c48e6868SAlexander Graf case 2: 1560c48e6868SAlexander Graf *val = fdt64_to_cpu(*prop64); 1561c48e6868SAlexander Graf break; 1562c48e6868SAlexander Graf default: 1563c48e6868SAlexander Graf return -FDT_ERR_NOSPACE; 1564c48e6868SAlexander Graf } 1565c48e6868SAlexander Graf 1566c48e6868SAlexander Graf return 0; 1567c48e6868SAlexander Graf } 1568c48e6868SAlexander Graf 1569c48e6868SAlexander Graf /** 1570c48e6868SAlexander Graf * fdt_read_range - Read a node's n'th range property 1571c48e6868SAlexander Graf * 1572c48e6868SAlexander Graf * @fdt: ptr to device tree 1573c48e6868SAlexander Graf * @node: offset of node 1574c48e6868SAlexander Graf * @n: range index 1575c48e6868SAlexander Graf * @child_addr: pointer to storage for the "child address" field 1576c48e6868SAlexander Graf * @addr: pointer to storage for the CPU view translated physical start 1577c48e6868SAlexander Graf * @len: pointer to storage for the range length 1578c48e6868SAlexander Graf * 1579c48e6868SAlexander Graf * Convenience function that reads and interprets a specific range out of 1580c48e6868SAlexander Graf * a number of the "ranges" property array. 1581c48e6868SAlexander Graf */ 1582c48e6868SAlexander Graf int fdt_read_range(void *fdt, int node, int n, uint64_t *child_addr, 1583c48e6868SAlexander Graf uint64_t *addr, uint64_t *len) 1584c48e6868SAlexander Graf { 1585c48e6868SAlexander Graf int pnode = fdt_parent_offset(fdt, node); 1586c48e6868SAlexander Graf const fdt32_t *ranges; 1587c48e6868SAlexander Graf int pacells; 1588c48e6868SAlexander Graf int acells; 1589c48e6868SAlexander Graf int scells; 1590c48e6868SAlexander Graf int ranges_len; 1591c48e6868SAlexander Graf int cell = 0; 1592c48e6868SAlexander Graf int r = 0; 1593c48e6868SAlexander Graf 1594c48e6868SAlexander Graf /* 1595c48e6868SAlexander Graf * The "ranges" property is an array of 1596c48e6868SAlexander Graf * { <child address> <parent address> <size in child address space> } 1597c48e6868SAlexander Graf * 1598c48e6868SAlexander Graf * All 3 elements can span a diffent number of cells. Fetch their size. 1599c48e6868SAlexander Graf */ 1600c48e6868SAlexander Graf pacells = fdt_getprop_u32_default_node(fdt, pnode, 0, "#address-cells", 1); 1601c48e6868SAlexander Graf acells = fdt_getprop_u32_default_node(fdt, node, 0, "#address-cells", 1); 1602c48e6868SAlexander Graf scells = fdt_getprop_u32_default_node(fdt, node, 0, "#size-cells", 1); 1603c48e6868SAlexander Graf 1604c48e6868SAlexander Graf /* Now try to get the ranges property */ 1605c48e6868SAlexander Graf ranges = fdt_getprop(fdt, node, "ranges", &ranges_len); 1606c48e6868SAlexander Graf if (!ranges) 1607c48e6868SAlexander Graf return -FDT_ERR_NOTFOUND; 1608c48e6868SAlexander Graf ranges_len /= sizeof(uint32_t); 1609c48e6868SAlexander Graf 1610c48e6868SAlexander Graf /* Jump to the n'th entry */ 1611c48e6868SAlexander Graf cell = n * (pacells + acells + scells); 1612c48e6868SAlexander Graf 1613c48e6868SAlexander Graf /* Read <child address> */ 1614c48e6868SAlexander Graf if (child_addr) { 1615c48e6868SAlexander Graf r = fdt_read_prop(ranges, ranges_len, cell, child_addr, 1616c48e6868SAlexander Graf acells); 1617c48e6868SAlexander Graf if (r) 1618c48e6868SAlexander Graf return r; 1619c48e6868SAlexander Graf } 1620c48e6868SAlexander Graf cell += acells; 1621c48e6868SAlexander Graf 1622c48e6868SAlexander Graf /* Read <parent address> */ 1623c48e6868SAlexander Graf if (addr) 1624c48e6868SAlexander Graf *addr = fdt_translate_address(fdt, node, ranges + cell); 1625c48e6868SAlexander Graf cell += pacells; 1626c48e6868SAlexander Graf 1627c48e6868SAlexander Graf /* Read <size in child address space> */ 1628c48e6868SAlexander Graf if (len) { 1629c48e6868SAlexander Graf r = fdt_read_prop(ranges, ranges_len, cell, len, scells); 1630c48e6868SAlexander Graf if (r) 1631c48e6868SAlexander Graf return r; 1632c48e6868SAlexander Graf } 1633c48e6868SAlexander Graf 1634c48e6868SAlexander Graf return 0; 1635c48e6868SAlexander Graf } 1636d4f495a8SHans de Goede 1637d4f495a8SHans de Goede /** 1638d4f495a8SHans de Goede * fdt_setup_simplefb_node - Fill and enable a simplefb node 1639d4f495a8SHans de Goede * 1640d4f495a8SHans de Goede * @fdt: ptr to device tree 1641d4f495a8SHans de Goede * @node: offset of the simplefb node 1642d4f495a8SHans de Goede * @base_address: framebuffer base address 1643d4f495a8SHans de Goede * @width: width in pixels 1644d4f495a8SHans de Goede * @height: height in pixels 1645d4f495a8SHans de Goede * @stride: bytes per line 1646d4f495a8SHans de Goede * @format: pixel format string 1647d4f495a8SHans de Goede * 1648d4f495a8SHans de Goede * Convenience function to fill and enable a simplefb node. 1649d4f495a8SHans de Goede */ 1650d4f495a8SHans de Goede int fdt_setup_simplefb_node(void *fdt, int node, u64 base_address, u32 width, 1651d4f495a8SHans de Goede u32 height, u32 stride, const char *format) 1652d4f495a8SHans de Goede { 1653d4f495a8SHans de Goede char name[32]; 1654d4f495a8SHans de Goede fdt32_t cells[4]; 1655d4f495a8SHans de Goede int i, addrc, sizec, ret; 1656d4f495a8SHans de Goede 1657eed36609SSimon Glass fdt_support_default_count_cells(fdt, fdt_parent_offset(fdt, node), 1658d4f495a8SHans de Goede &addrc, &sizec); 1659d4f495a8SHans de Goede i = 0; 1660d4f495a8SHans de Goede if (addrc == 2) 1661d4f495a8SHans de Goede cells[i++] = cpu_to_fdt32(base_address >> 32); 1662d4f495a8SHans de Goede cells[i++] = cpu_to_fdt32(base_address); 1663d4f495a8SHans de Goede if (sizec == 2) 1664d4f495a8SHans de Goede cells[i++] = 0; 1665d4f495a8SHans de Goede cells[i++] = cpu_to_fdt32(height * stride); 1666d4f495a8SHans de Goede 1667d4f495a8SHans de Goede ret = fdt_setprop(fdt, node, "reg", cells, sizeof(cells[0]) * i); 1668d4f495a8SHans de Goede if (ret < 0) 1669d4f495a8SHans de Goede return ret; 1670d4f495a8SHans de Goede 1671c3ec646dSSimon Glass snprintf(name, sizeof(name), "framebuffer@%" PRIx64, base_address); 1672d4f495a8SHans de Goede ret = fdt_set_name(fdt, node, name); 1673d4f495a8SHans de Goede if (ret < 0) 1674d4f495a8SHans de Goede return ret; 1675d4f495a8SHans de Goede 1676d4f495a8SHans de Goede ret = fdt_setprop_u32(fdt, node, "width", width); 1677d4f495a8SHans de Goede if (ret < 0) 1678d4f495a8SHans de Goede return ret; 1679d4f495a8SHans de Goede 1680d4f495a8SHans de Goede ret = fdt_setprop_u32(fdt, node, "height", height); 1681d4f495a8SHans de Goede if (ret < 0) 1682d4f495a8SHans de Goede return ret; 1683d4f495a8SHans de Goede 1684d4f495a8SHans de Goede ret = fdt_setprop_u32(fdt, node, "stride", stride); 1685d4f495a8SHans de Goede if (ret < 0) 1686d4f495a8SHans de Goede return ret; 1687d4f495a8SHans de Goede 1688d4f495a8SHans de Goede ret = fdt_setprop_string(fdt, node, "format", format); 1689d4f495a8SHans de Goede if (ret < 0) 1690d4f495a8SHans de Goede return ret; 1691d4f495a8SHans de Goede 1692d4f495a8SHans de Goede ret = fdt_setprop_string(fdt, node, "status", "okay"); 1693d4f495a8SHans de Goede if (ret < 0) 1694d4f495a8SHans de Goede return ret; 1695d4f495a8SHans de Goede 1696d4f495a8SHans de Goede return 0; 1697d4f495a8SHans de Goede } 169808daa258STim Harvey 169908daa258STim Harvey /* 170008daa258STim Harvey * Update native-mode in display-timings from display environment variable. 170108daa258STim Harvey * The node to update are specified by path. 170208daa258STim Harvey */ 170308daa258STim Harvey int fdt_fixup_display(void *blob, const char *path, const char *display) 170408daa258STim Harvey { 170508daa258STim Harvey int off, toff; 170608daa258STim Harvey 170708daa258STim Harvey if (!display || !path) 170808daa258STim Harvey return -FDT_ERR_NOTFOUND; 170908daa258STim Harvey 171008daa258STim Harvey toff = fdt_path_offset(blob, path); 171108daa258STim Harvey if (toff >= 0) 171208daa258STim Harvey toff = fdt_subnode_offset(blob, toff, "display-timings"); 171308daa258STim Harvey if (toff < 0) 171408daa258STim Harvey return toff; 171508daa258STim Harvey 171608daa258STim Harvey for (off = fdt_first_subnode(blob, toff); 171708daa258STim Harvey off >= 0; 171808daa258STim Harvey off = fdt_next_subnode(blob, off)) { 171908daa258STim Harvey uint32_t h = fdt_get_phandle(blob, off); 172008daa258STim Harvey debug("%s:0x%x\n", fdt_get_name(blob, off, NULL), 172108daa258STim Harvey fdt32_to_cpu(h)); 172208daa258STim Harvey if (strcasecmp(fdt_get_name(blob, off, NULL), display) == 0) 172308daa258STim Harvey return fdt_setprop_u32(blob, toff, "native-mode", h); 172408daa258STim Harvey } 172508daa258STim Harvey return toff; 172608daa258STim Harvey } 1727fc7c3189SPantelis Antoniou 1728fc7c3189SPantelis Antoniou #ifdef CONFIG_OF_LIBFDT_OVERLAY 1729fc7c3189SPantelis Antoniou /** 1730fc7c3189SPantelis Antoniou * fdt_overlay_apply_verbose - Apply an overlay with verbose error reporting 1731fc7c3189SPantelis Antoniou * 1732fc7c3189SPantelis Antoniou * @fdt: ptr to device tree 1733fc7c3189SPantelis Antoniou * @fdto: ptr to device tree overlay 1734fc7c3189SPantelis Antoniou * 1735fc7c3189SPantelis Antoniou * Convenience function to apply an overlay and display helpful messages 1736fc7c3189SPantelis Antoniou * in the case of an error 1737fc7c3189SPantelis Antoniou */ 1738fc7c3189SPantelis Antoniou int fdt_overlay_apply_verbose(void *fdt, void *fdto) 1739fc7c3189SPantelis Antoniou { 1740fc7c3189SPantelis Antoniou int err; 1741fc7c3189SPantelis Antoniou bool has_symbols; 1742fc7c3189SPantelis Antoniou 1743fc7c3189SPantelis Antoniou err = fdt_path_offset(fdt, "/__symbols__"); 1744fc7c3189SPantelis Antoniou has_symbols = err >= 0; 1745fc7c3189SPantelis Antoniou 1746fc7c3189SPantelis Antoniou err = fdt_overlay_apply(fdt, fdto); 1747fc7c3189SPantelis Antoniou if (err < 0) { 1748fc7c3189SPantelis Antoniou printf("failed on fdt_overlay_apply(): %s\n", 1749fc7c3189SPantelis Antoniou fdt_strerror(err)); 1750fc7c3189SPantelis Antoniou if (!has_symbols) { 1751fc7c3189SPantelis Antoniou printf("base fdt does did not have a /__symbols__ node\n"); 1752fc7c3189SPantelis Antoniou printf("make sure you've compiled with -@\n"); 1753fc7c3189SPantelis Antoniou } 1754fc7c3189SPantelis Antoniou } 1755fc7c3189SPantelis Antoniou return err; 1756fc7c3189SPantelis Antoniou } 1757fc7c3189SPantelis Antoniou #endif 1758