164dbbd40SGerald Van Baren /* 264dbbd40SGerald Van Baren * (C) Copyright 2007 364dbbd40SGerald Van Baren * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com 464dbbd40SGerald Van Baren * 52a523f52SShengzhou Liu * Copyright 2010-2011 Freescale Semiconductor, Inc. 6a0342c08SKumar Gala * 71a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 864dbbd40SGerald Van Baren */ 964dbbd40SGerald Van Baren 1064dbbd40SGerald Van Baren #include <common.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> 1564dbbd40SGerald Van Baren #include <libfdt.h> 1664dbbd40SGerald Van Baren #include <fdt_support.h> 17151c8b09SKumar Gala #include <exports.h> 1864dbbd40SGerald Van Baren 1964dbbd40SGerald Van Baren /* 20f77a606aSDavid Feng * Get cells len in bytes 21f77a606aSDavid Feng * if #NNNN-cells property is 2 then len is 8 22f77a606aSDavid Feng * otherwise len is 4 23f77a606aSDavid Feng */ 24f89d482fSMasahiro Yamada static int get_cells_len(const void *fdt, const char *nr_cells_name) 25f77a606aSDavid Feng { 26f77a606aSDavid Feng const fdt32_t *cell; 27f77a606aSDavid Feng 28f89d482fSMasahiro Yamada cell = fdt_getprop(fdt, 0, nr_cells_name, NULL); 29f77a606aSDavid Feng if (cell && fdt32_to_cpu(*cell) == 2) 30f77a606aSDavid Feng return 8; 31f77a606aSDavid Feng 32f77a606aSDavid Feng return 4; 33f77a606aSDavid Feng } 34f77a606aSDavid Feng 353bed2aafSKumar Gala /** 3694fb182cSAlexander Graf * fdt_getprop_u32_default_node - Return a node's property or a default 3794fb182cSAlexander Graf * 3894fb182cSAlexander Graf * @fdt: ptr to device tree 3994fb182cSAlexander Graf * @off: offset of node 4094fb182cSAlexander Graf * @cell: cell offset in property 4194fb182cSAlexander Graf * @prop: property name 4294fb182cSAlexander Graf * @dflt: default value if the property isn't found 4394fb182cSAlexander Graf * 4494fb182cSAlexander Graf * Convenience function to return a node's property or a default value if 4594fb182cSAlexander Graf * the property doesn't exist. 4694fb182cSAlexander Graf */ 4794fb182cSAlexander Graf u32 fdt_getprop_u32_default_node(const void *fdt, int off, int cell, 4894fb182cSAlexander Graf const char *prop, const u32 dflt) 4994fb182cSAlexander Graf { 5094fb182cSAlexander Graf const fdt32_t *val; 5194fb182cSAlexander Graf int len; 5294fb182cSAlexander Graf 5394fb182cSAlexander Graf val = fdt_getprop(fdt, off, prop, &len); 5494fb182cSAlexander Graf 5594fb182cSAlexander Graf /* Check if property exists */ 5694fb182cSAlexander Graf if (!val) 5794fb182cSAlexander Graf return dflt; 5894fb182cSAlexander Graf 5994fb182cSAlexander Graf /* Check if property is long enough */ 6094fb182cSAlexander Graf if (len < ((cell + 1) * sizeof(uint32_t))) 6194fb182cSAlexander Graf return dflt; 6294fb182cSAlexander Graf 6394fb182cSAlexander Graf return fdt32_to_cpu(*val); 6494fb182cSAlexander Graf } 6594fb182cSAlexander Graf 6694fb182cSAlexander Graf /** 673bed2aafSKumar Gala * fdt_getprop_u32_default - Find a node and return it's property or a default 683bed2aafSKumar Gala * 693bed2aafSKumar Gala * @fdt: ptr to device tree 703bed2aafSKumar Gala * @path: path of node 713bed2aafSKumar Gala * @prop: property name 723bed2aafSKumar Gala * @dflt: default value if the property isn't found 733bed2aafSKumar Gala * 743bed2aafSKumar Gala * Convenience function to find a node and return it's property or a 753bed2aafSKumar Gala * default value if it doesn't exist. 763bed2aafSKumar Gala */ 7707e12784SGabe Black u32 fdt_getprop_u32_default(const void *fdt, const char *path, 7807e12784SGabe Black const char *prop, const u32 dflt) 793bed2aafSKumar Gala { 803bed2aafSKumar Gala int off; 813bed2aafSKumar Gala 823bed2aafSKumar Gala off = fdt_path_offset(fdt, path); 833bed2aafSKumar Gala if (off < 0) 843bed2aafSKumar Gala return dflt; 853bed2aafSKumar Gala 8694fb182cSAlexander Graf return fdt_getprop_u32_default_node(fdt, off, 0, prop, dflt); 873bed2aafSKumar Gala } 8864dbbd40SGerald Van Baren 89a3c2933eSKumar Gala /** 90a3c2933eSKumar Gala * fdt_find_and_setprop: Find a node and set it's property 91a3c2933eSKumar Gala * 92a3c2933eSKumar Gala * @fdt: ptr to device tree 93a3c2933eSKumar Gala * @node: path of node 94a3c2933eSKumar Gala * @prop: property name 95a3c2933eSKumar Gala * @val: ptr to new value 96a3c2933eSKumar Gala * @len: length of new property value 97a3c2933eSKumar Gala * @create: flag to create the property if it doesn't exist 98a3c2933eSKumar Gala * 99a3c2933eSKumar Gala * Convenience function to directly set a property given the path to the node. 100a3c2933eSKumar Gala */ 101a3c2933eSKumar Gala int fdt_find_and_setprop(void *fdt, const char *node, const char *prop, 102a3c2933eSKumar Gala const void *val, int len, int create) 103a3c2933eSKumar Gala { 1048d04f02fSKumar Gala int nodeoff = fdt_path_offset(fdt, node); 105a3c2933eSKumar Gala 106a3c2933eSKumar Gala if (nodeoff < 0) 107a3c2933eSKumar Gala return nodeoff; 108a3c2933eSKumar Gala 1098aa5ec6eSKim Phillips if ((!create) && (fdt_get_property(fdt, nodeoff, prop, NULL) == NULL)) 110a3c2933eSKumar Gala return 0; /* create flag not set; so exit quietly */ 111a3c2933eSKumar Gala 112a3c2933eSKumar Gala return fdt_setprop(fdt, nodeoff, prop, val, len); 113a3c2933eSKumar Gala } 114a3c2933eSKumar Gala 1158edb2192SMasahiro Yamada /** 116*a9e8e291SSimon Glass * fdt_find_or_add_subnode() - find or possibly add a subnode of a given node 117*a9e8e291SSimon Glass * 1188edb2192SMasahiro Yamada * @fdt: pointer to the device tree blob 1198edb2192SMasahiro Yamada * @parentoffset: structure block offset of a node 1208edb2192SMasahiro Yamada * @name: name of the subnode to locate 1218edb2192SMasahiro Yamada * 1228edb2192SMasahiro Yamada * fdt_subnode_offset() finds a subnode of the node with a given name. 1238edb2192SMasahiro Yamada * If the subnode does not exist, it will be created. 1248edb2192SMasahiro Yamada */ 125*a9e8e291SSimon Glass int fdt_find_or_add_subnode(void *fdt, int parentoffset, const char *name) 1268edb2192SMasahiro Yamada { 1278edb2192SMasahiro Yamada int offset; 1288edb2192SMasahiro Yamada 1298edb2192SMasahiro Yamada offset = fdt_subnode_offset(fdt, parentoffset, name); 1308edb2192SMasahiro Yamada 1318edb2192SMasahiro Yamada if (offset == -FDT_ERR_NOTFOUND) 1328edb2192SMasahiro Yamada offset = fdt_add_subnode(fdt, parentoffset, name); 1338edb2192SMasahiro Yamada 1348edb2192SMasahiro Yamada if (offset < 0) 1358edb2192SMasahiro Yamada printf("%s: %s: %s\n", __func__, name, fdt_strerror(offset)); 1368edb2192SMasahiro Yamada 1378edb2192SMasahiro Yamada return offset; 1388edb2192SMasahiro Yamada } 1398edb2192SMasahiro Yamada 140972f2a89SMasahiro Yamada /* rename to CONFIG_OF_STDOUT_PATH ? */ 141972f2a89SMasahiro Yamada #if defined(OF_STDOUT_PATH) 142972f2a89SMasahiro Yamada static int fdt_fixup_stdout(void *fdt, int chosenoff) 143972f2a89SMasahiro Yamada { 144972f2a89SMasahiro Yamada return fdt_setprop(fdt, chosenoff, "linux,stdout-path", 145972f2a89SMasahiro Yamada OF_STDOUT_PATH, strlen(OF_STDOUT_PATH) + 1); 146972f2a89SMasahiro Yamada } 147972f2a89SMasahiro Yamada #elif defined(CONFIG_OF_STDOUT_VIA_ALIAS) && defined(CONFIG_CONS_INDEX) 1483e303f74SAnton Vorontsov static void fdt_fill_multisername(char *sername, size_t maxlen) 1493e303f74SAnton Vorontsov { 1503e303f74SAnton Vorontsov const char *outname = stdio_devices[stdout]->name; 1513e303f74SAnton Vorontsov 1523e303f74SAnton Vorontsov if (strcmp(outname, "serial") > 0) 1533e303f74SAnton Vorontsov strncpy(sername, outname, maxlen); 1543e303f74SAnton Vorontsov 1553e303f74SAnton Vorontsov /* eserial? */ 1563e303f74SAnton Vorontsov if (strcmp(outname + 1, "serial") > 0) 1573e303f74SAnton Vorontsov strncpy(sername, outname + 1, maxlen); 1583e303f74SAnton Vorontsov } 1593e303f74SAnton Vorontsov 16040777812SDetlev Zundel static int fdt_fixup_stdout(void *fdt, int chosenoff) 161151c8b09SKumar Gala { 162972f2a89SMasahiro Yamada int err; 163972f2a89SMasahiro Yamada int aliasoff; 164151c8b09SKumar Gala char sername[9] = { 0 }; 165972f2a89SMasahiro Yamada const void *path; 166972f2a89SMasahiro Yamada int len; 167972f2a89SMasahiro Yamada char tmp[256]; /* long enough */ 168151c8b09SKumar Gala 1693e303f74SAnton Vorontsov fdt_fill_multisername(sername, sizeof(sername) - 1); 1703e303f74SAnton Vorontsov if (!sername[0]) 171151c8b09SKumar Gala sprintf(sername, "serial%d", CONFIG_CONS_INDEX - 1); 172151c8b09SKumar Gala 173972f2a89SMasahiro Yamada aliasoff = fdt_path_offset(fdt, "/aliases"); 174972f2a89SMasahiro Yamada if (aliasoff < 0) { 175972f2a89SMasahiro Yamada err = aliasoff; 176972f2a89SMasahiro Yamada goto error; 177151c8b09SKumar Gala } 178972f2a89SMasahiro Yamada 179972f2a89SMasahiro Yamada path = fdt_getprop(fdt, aliasoff, sername, &len); 180972f2a89SMasahiro Yamada if (!path) { 181151c8b09SKumar Gala err = len; 182972f2a89SMasahiro Yamada goto error; 183151c8b09SKumar Gala } 184972f2a89SMasahiro Yamada 185972f2a89SMasahiro Yamada /* fdt_setprop may break "path" so we copy it to tmp buffer */ 186972f2a89SMasahiro Yamada memcpy(tmp, path, len); 187972f2a89SMasahiro Yamada 188972f2a89SMasahiro Yamada err = fdt_setprop(fdt, chosenoff, "linux,stdout-path", tmp, len); 189972f2a89SMasahiro Yamada error: 190151c8b09SKumar Gala if (err < 0) 191151c8b09SKumar Gala printf("WARNING: could not set linux,stdout-path %s.\n", 192151c8b09SKumar Gala fdt_strerror(err)); 193151c8b09SKumar Gala 194151c8b09SKumar Gala return err; 195151c8b09SKumar Gala } 196972f2a89SMasahiro Yamada #else 197972f2a89SMasahiro Yamada static int fdt_fixup_stdout(void *fdt, int chosenoff) 198972f2a89SMasahiro Yamada { 199972f2a89SMasahiro Yamada return 0; 200972f2a89SMasahiro Yamada } 201151c8b09SKumar Gala #endif 202151c8b09SKumar Gala 203f18295d3SMasahiro Yamada static inline int fdt_setprop_uxx(void *fdt, int nodeoffset, const char *name, 204f18295d3SMasahiro Yamada uint64_t val, int is_u64) 205f18295d3SMasahiro Yamada { 206f18295d3SMasahiro Yamada if (is_u64) 207f18295d3SMasahiro Yamada return fdt_setprop_u64(fdt, nodeoffset, name, val); 208f18295d3SMasahiro Yamada else 209f18295d3SMasahiro Yamada return fdt_setprop_u32(fdt, nodeoffset, name, (uint32_t)val); 210f18295d3SMasahiro Yamada } 211f18295d3SMasahiro Yamada 212f18295d3SMasahiro Yamada 213dbe963aeSMasahiro Yamada int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end) 21464dbbd40SGerald Van Baren { 215f18295d3SMasahiro Yamada int nodeoffset; 2162a1a2cb6SKumar Gala int err, j, total; 217f18295d3SMasahiro Yamada int is_u64; 2182a1a2cb6SKumar Gala uint64_t addr, size; 21964dbbd40SGerald Van Baren 22050babaf8SMasahiro Yamada /* just return if the size of initrd is zero */ 22150babaf8SMasahiro Yamada if (initrd_start == initrd_end) 22250babaf8SMasahiro Yamada return 0; 22350babaf8SMasahiro Yamada 2248edb2192SMasahiro Yamada /* find or create "/chosen" node. */ 2258edb2192SMasahiro Yamada nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen"); 2268edb2192SMasahiro Yamada if (nodeoffset < 0) 2272a1a2cb6SKumar Gala return nodeoffset; 22864dbbd40SGerald Van Baren 2292a1a2cb6SKumar Gala total = fdt_num_mem_rsv(fdt); 230c28abb9cSGerald Van Baren 231c28abb9cSGerald Van Baren /* 232c28abb9cSGerald Van Baren * Look for an existing entry and update it. If we don't find 233c28abb9cSGerald Van Baren * the entry, we will j be the next available slot. 234c28abb9cSGerald Van Baren */ 2358d04f02fSKumar Gala for (j = 0; j < total; j++) { 2368d04f02fSKumar Gala err = fdt_get_mem_rsv(fdt, j, &addr, &size); 2378d04f02fSKumar Gala if (addr == initrd_start) { 2388d04f02fSKumar Gala fdt_del_mem_rsv(fdt, j); 239c28abb9cSGerald Van Baren break; 240c28abb9cSGerald Van Baren } 241c28abb9cSGerald Van Baren } 2428d04f02fSKumar Gala 243ce6b27a8SGrant Likely err = fdt_add_mem_rsv(fdt, initrd_start, initrd_end - initrd_start); 24464dbbd40SGerald Van Baren if (err < 0) { 2452a1a2cb6SKumar Gala printf("fdt_initrd: %s\n", fdt_strerror(err)); 24664dbbd40SGerald Van Baren return err; 24764dbbd40SGerald Van Baren } 2482a1a2cb6SKumar Gala 249f18295d3SMasahiro Yamada is_u64 = (get_cells_len(fdt, "#address-cells") == 8); 250f77a606aSDavid Feng 251f18295d3SMasahiro Yamada err = fdt_setprop_uxx(fdt, nodeoffset, "linux,initrd-start", 252f18295d3SMasahiro Yamada (uint64_t)initrd_start, is_u64); 253f18295d3SMasahiro Yamada 2542a1a2cb6SKumar Gala if (err < 0) { 255dbe963aeSMasahiro Yamada printf("WARNING: could not set linux,initrd-start %s.\n", 2562a1a2cb6SKumar Gala fdt_strerror(err)); 2572a1a2cb6SKumar Gala return err; 2582a1a2cb6SKumar Gala } 259f18295d3SMasahiro Yamada 260f18295d3SMasahiro Yamada err = fdt_setprop_uxx(fdt, nodeoffset, "linux,initrd-end", 261f18295d3SMasahiro Yamada (uint64_t)initrd_end, is_u64); 262f18295d3SMasahiro Yamada 2632a1a2cb6SKumar Gala if (err < 0) { 2642a1a2cb6SKumar Gala printf("WARNING: could not set linux,initrd-end %s.\n", 2652a1a2cb6SKumar Gala fdt_strerror(err)); 2662a1a2cb6SKumar Gala 2672a1a2cb6SKumar Gala return err; 2682a1a2cb6SKumar Gala } 2692a1a2cb6SKumar Gala 2702a1a2cb6SKumar Gala return 0; 2712a1a2cb6SKumar Gala } 2722a1a2cb6SKumar Gala 273bc6ed0f9SMasahiro Yamada int fdt_chosen(void *fdt) 2742a1a2cb6SKumar Gala { 2752a1a2cb6SKumar Gala int nodeoffset; 2762a1a2cb6SKumar Gala int err; 2772a1a2cb6SKumar Gala char *str; /* used to set string properties */ 2782a1a2cb6SKumar Gala 2792a1a2cb6SKumar Gala err = fdt_check_header(fdt); 2802a1a2cb6SKumar Gala if (err < 0) { 2812a1a2cb6SKumar Gala printf("fdt_chosen: %s\n", fdt_strerror(err)); 2822a1a2cb6SKumar Gala return err; 28364dbbd40SGerald Van Baren } 28464dbbd40SGerald Van Baren 2858edb2192SMasahiro Yamada /* find or create "/chosen" node. */ 2868edb2192SMasahiro Yamada nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen"); 2878edb2192SMasahiro Yamada if (nodeoffset < 0) 28864dbbd40SGerald Van Baren return nodeoffset; 28964dbbd40SGerald Van Baren 29064dbbd40SGerald Van Baren str = getenv("bootargs"); 291972f2a89SMasahiro Yamada if (str) { 292972f2a89SMasahiro Yamada err = fdt_setprop(fdt, nodeoffset, "bootargs", str, 293972f2a89SMasahiro Yamada strlen(str) + 1); 294972f2a89SMasahiro Yamada if (err < 0) { 2955fe6be62SGerald Van Baren printf("WARNING: could not set bootargs %s.\n", 29635ec398fSGerald Van Baren fdt_strerror(err)); 297972f2a89SMasahiro Yamada return err; 298972f2a89SMasahiro Yamada } 29964dbbd40SGerald Van Baren } 3002a1a2cb6SKumar Gala 301972f2a89SMasahiro Yamada return fdt_fixup_stdout(fdt, nodeoffset); 30264dbbd40SGerald Van Baren } 30364dbbd40SGerald Van Baren 304e93becf8SKumar Gala void do_fixup_by_path(void *fdt, const char *path, const char *prop, 305e93becf8SKumar Gala const void *val, int len, int create) 306e93becf8SKumar Gala { 307e93becf8SKumar Gala #if defined(DEBUG) 308e93becf8SKumar Gala int i; 309d9ad115bSKumar Gala debug("Updating property '%s/%s' = ", path, prop); 310e93becf8SKumar Gala for (i = 0; i < len; i++) 311e93becf8SKumar Gala debug(" %.2x", *(u8*)(val+i)); 312e93becf8SKumar Gala debug("\n"); 313e93becf8SKumar Gala #endif 314e93becf8SKumar Gala int rc = fdt_find_and_setprop(fdt, path, prop, val, len, create); 315e93becf8SKumar Gala if (rc) 316e93becf8SKumar Gala printf("Unable to update property %s:%s, err=%s\n", 317e93becf8SKumar Gala path, prop, fdt_strerror(rc)); 318e93becf8SKumar Gala } 319e93becf8SKumar Gala 320e93becf8SKumar Gala void do_fixup_by_path_u32(void *fdt, const char *path, const char *prop, 321e93becf8SKumar Gala u32 val, int create) 322e93becf8SKumar Gala { 3238aa5ec6eSKim Phillips fdt32_t tmp = cpu_to_fdt32(val); 3248aa5ec6eSKim Phillips do_fixup_by_path(fdt, path, prop, &tmp, sizeof(tmp), create); 325e93becf8SKumar Gala } 326e93becf8SKumar Gala 3279eb77ceaSKumar Gala void do_fixup_by_prop(void *fdt, 3289eb77ceaSKumar Gala const char *pname, const void *pval, int plen, 3299eb77ceaSKumar Gala const char *prop, const void *val, int len, 3309eb77ceaSKumar Gala int create) 3319eb77ceaSKumar Gala { 3329eb77ceaSKumar Gala int off; 3339eb77ceaSKumar Gala #if defined(DEBUG) 3349eb77ceaSKumar Gala int i; 335d9ad115bSKumar Gala debug("Updating property '%s' = ", prop); 3369eb77ceaSKumar Gala for (i = 0; i < len; i++) 3379eb77ceaSKumar Gala debug(" %.2x", *(u8*)(val+i)); 3389eb77ceaSKumar Gala debug("\n"); 3399eb77ceaSKumar Gala #endif 3409eb77ceaSKumar Gala off = fdt_node_offset_by_prop_value(fdt, -1, pname, pval, plen); 3419eb77ceaSKumar Gala while (off != -FDT_ERR_NOTFOUND) { 3428aa5ec6eSKim Phillips if (create || (fdt_get_property(fdt, off, prop, NULL) != NULL)) 3439eb77ceaSKumar Gala fdt_setprop(fdt, off, prop, val, len); 3449eb77ceaSKumar Gala off = fdt_node_offset_by_prop_value(fdt, off, pname, pval, plen); 3459eb77ceaSKumar Gala } 3469eb77ceaSKumar Gala } 3479eb77ceaSKumar Gala 3489eb77ceaSKumar Gala void do_fixup_by_prop_u32(void *fdt, 3499eb77ceaSKumar Gala const char *pname, const void *pval, int plen, 3509eb77ceaSKumar Gala const char *prop, u32 val, int create) 3519eb77ceaSKumar Gala { 3528aa5ec6eSKim Phillips fdt32_t tmp = cpu_to_fdt32(val); 3538aa5ec6eSKim Phillips do_fixup_by_prop(fdt, pname, pval, plen, prop, &tmp, 4, create); 3549eb77ceaSKumar Gala } 3559eb77ceaSKumar Gala 3569eb77ceaSKumar Gala void do_fixup_by_compat(void *fdt, const char *compat, 3579eb77ceaSKumar Gala const char *prop, const void *val, int len, int create) 3589eb77ceaSKumar Gala { 3599eb77ceaSKumar Gala int off = -1; 3609eb77ceaSKumar Gala #if defined(DEBUG) 3619eb77ceaSKumar Gala int i; 362d9ad115bSKumar Gala debug("Updating property '%s' = ", prop); 3639eb77ceaSKumar Gala for (i = 0; i < len; i++) 3649eb77ceaSKumar Gala debug(" %.2x", *(u8*)(val+i)); 3659eb77ceaSKumar Gala debug("\n"); 3669eb77ceaSKumar Gala #endif 3679eb77ceaSKumar Gala off = fdt_node_offset_by_compatible(fdt, -1, compat); 3689eb77ceaSKumar Gala while (off != -FDT_ERR_NOTFOUND) { 3698aa5ec6eSKim Phillips if (create || (fdt_get_property(fdt, off, prop, NULL) != NULL)) 3709eb77ceaSKumar Gala fdt_setprop(fdt, off, prop, val, len); 3719eb77ceaSKumar Gala off = fdt_node_offset_by_compatible(fdt, off, compat); 3729eb77ceaSKumar Gala } 3739eb77ceaSKumar Gala } 3749eb77ceaSKumar Gala 3759eb77ceaSKumar Gala void do_fixup_by_compat_u32(void *fdt, const char *compat, 3769eb77ceaSKumar Gala const char *prop, u32 val, int create) 3779eb77ceaSKumar Gala { 3788aa5ec6eSKim Phillips fdt32_t tmp = cpu_to_fdt32(val); 3798aa5ec6eSKim Phillips do_fixup_by_compat(fdt, compat, prop, &tmp, 4, create); 3809eb77ceaSKumar Gala } 3819eb77ceaSKumar Gala 382739a01edSMasahiro Yamada /* 383739a01edSMasahiro Yamada * fdt_pack_reg - pack address and size array into the "reg"-suitable stream 384739a01edSMasahiro Yamada */ 385739a01edSMasahiro Yamada static int fdt_pack_reg(const void *fdt, void *buf, uint64_t *address, 386739a01edSMasahiro Yamada uint64_t *size, int n) 387739a01edSMasahiro Yamada { 388739a01edSMasahiro Yamada int i; 389739a01edSMasahiro Yamada int address_len = get_cells_len(fdt, "#address-cells"); 390739a01edSMasahiro Yamada int size_len = get_cells_len(fdt, "#size-cells"); 391739a01edSMasahiro Yamada char *p = buf; 392739a01edSMasahiro Yamada 393739a01edSMasahiro Yamada for (i = 0; i < n; i++) { 394739a01edSMasahiro Yamada if (address_len == 8) 395739a01edSMasahiro Yamada *(fdt64_t *)p = cpu_to_fdt64(address[i]); 396739a01edSMasahiro Yamada else 397739a01edSMasahiro Yamada *(fdt32_t *)p = cpu_to_fdt32(address[i]); 398739a01edSMasahiro Yamada p += address_len; 399739a01edSMasahiro Yamada 400739a01edSMasahiro Yamada if (size_len == 8) 401739a01edSMasahiro Yamada *(fdt64_t *)p = cpu_to_fdt64(size[i]); 402739a01edSMasahiro Yamada else 403739a01edSMasahiro Yamada *(fdt32_t *)p = cpu_to_fdt32(size[i]); 404739a01edSMasahiro Yamada p += size_len; 405739a01edSMasahiro Yamada } 406739a01edSMasahiro Yamada 407739a01edSMasahiro Yamada return p - (char *)buf; 408739a01edSMasahiro Yamada } 409739a01edSMasahiro Yamada 4105e574546SDoug Anderson #ifdef CONFIG_NR_DRAM_BANKS 4115e574546SDoug Anderson #define MEMORY_BANKS_MAX CONFIG_NR_DRAM_BANKS 4125e574546SDoug Anderson #else 4138aa5ec6eSKim Phillips #define MEMORY_BANKS_MAX 4 4145e574546SDoug Anderson #endif 415a6bd9e83SJohn Rigby int fdt_fixup_memory_banks(void *blob, u64 start[], u64 size[], int banks) 416a6bd9e83SJohn Rigby { 417a6bd9e83SJohn Rigby int err, nodeoffset; 418739a01edSMasahiro Yamada int len; 4198aa5ec6eSKim Phillips u8 tmp[MEMORY_BANKS_MAX * 16]; /* Up to 64-bit address + 64-bit size */ 4203c927281SKumar Gala 4218aa5ec6eSKim Phillips if (banks > MEMORY_BANKS_MAX) { 4228aa5ec6eSKim Phillips printf("%s: num banks %d exceeds hardcoded limit %d." 4238aa5ec6eSKim Phillips " Recompile with higher MEMORY_BANKS_MAX?\n", 4248aa5ec6eSKim Phillips __FUNCTION__, banks, MEMORY_BANKS_MAX); 4258aa5ec6eSKim Phillips return -1; 4268aa5ec6eSKim Phillips } 4278aa5ec6eSKim Phillips 4283c927281SKumar Gala err = fdt_check_header(blob); 4293c927281SKumar Gala if (err < 0) { 4303c927281SKumar Gala printf("%s: %s\n", __FUNCTION__, fdt_strerror(err)); 4313c927281SKumar Gala return err; 4323c927281SKumar Gala } 4333c927281SKumar Gala 4348edb2192SMasahiro Yamada /* find or create "/memory" node. */ 4358edb2192SMasahiro Yamada nodeoffset = fdt_find_or_add_subnode(blob, 0, "memory"); 4368edb2192SMasahiro Yamada if (nodeoffset < 0) 4373c927281SKumar Gala return nodeoffset; 4388edb2192SMasahiro Yamada 4393c927281SKumar Gala err = fdt_setprop(blob, nodeoffset, "device_type", "memory", 4403c927281SKumar Gala sizeof("memory")); 4413c927281SKumar Gala if (err < 0) { 4423c927281SKumar Gala printf("WARNING: could not set %s %s.\n", "device_type", 4433c927281SKumar Gala fdt_strerror(err)); 4443c927281SKumar Gala return err; 4453c927281SKumar Gala } 4463c927281SKumar Gala 447739a01edSMasahiro Yamada len = fdt_pack_reg(blob, tmp, start, size, banks); 4483c927281SKumar Gala 4493c927281SKumar Gala err = fdt_setprop(blob, nodeoffset, "reg", tmp, len); 4503c927281SKumar Gala if (err < 0) { 4513c927281SKumar Gala printf("WARNING: could not set %s %s.\n", 4523c927281SKumar Gala "reg", fdt_strerror(err)); 4533c927281SKumar Gala return err; 4543c927281SKumar Gala } 4553c927281SKumar Gala return 0; 4563c927281SKumar Gala } 4573c927281SKumar Gala 458a6bd9e83SJohn Rigby int fdt_fixup_memory(void *blob, u64 start, u64 size) 459a6bd9e83SJohn Rigby { 460a6bd9e83SJohn Rigby return fdt_fixup_memory_banks(blob, &start, &size, 1); 461a6bd9e83SJohn Rigby } 462a6bd9e83SJohn Rigby 463ba37aa03SKumar Gala void fdt_fixup_ethernet(void *fdt) 464ab544633SKumar Gala { 465ba37aa03SKumar Gala int node, i, j; 466ba37aa03SKumar Gala char enet[16], *tmp, *end; 467064d55f8SStephen Warren char mac[16]; 468ab544633SKumar Gala const char *path; 469ba37aa03SKumar Gala unsigned char mac_addr[6]; 470ab544633SKumar Gala 471ab544633SKumar Gala node = fdt_path_offset(fdt, "/aliases"); 472ba37aa03SKumar Gala if (node < 0) 473ba37aa03SKumar Gala return; 474ba37aa03SKumar Gala 475b1f49ab8SDan Murphy if (!getenv("ethaddr")) { 476b1f49ab8SDan Murphy if (getenv("usbethaddr")) { 477b1f49ab8SDan Murphy strcpy(mac, "usbethaddr"); 478b1f49ab8SDan Murphy } else { 479b1f49ab8SDan Murphy debug("No ethernet MAC Address defined\n"); 480b1f49ab8SDan Murphy return; 481b1f49ab8SDan Murphy } 482b1f49ab8SDan Murphy } else { 483064d55f8SStephen Warren strcpy(mac, "ethaddr"); 484b1f49ab8SDan Murphy } 485b1f49ab8SDan Murphy 486b1f49ab8SDan Murphy i = 0; 487ba37aa03SKumar Gala while ((tmp = getenv(mac)) != NULL) { 488ba37aa03SKumar Gala sprintf(enet, "ethernet%d", i); 489ba37aa03SKumar Gala path = fdt_getprop(fdt, node, enet, NULL); 490ba37aa03SKumar Gala if (!path) { 491ba37aa03SKumar Gala debug("No alias for %s\n", enet); 492ba37aa03SKumar Gala sprintf(mac, "eth%daddr", ++i); 493ba37aa03SKumar Gala continue; 494ba37aa03SKumar Gala } 495ba37aa03SKumar Gala 496ba37aa03SKumar Gala for (j = 0; j < 6; j++) { 497ba37aa03SKumar Gala mac_addr[j] = tmp ? simple_strtoul(tmp, &end, 16) : 0; 498ba37aa03SKumar Gala if (tmp) 499ba37aa03SKumar Gala tmp = (*end) ? end+1 : end; 500ba37aa03SKumar Gala } 501ba37aa03SKumar Gala 502ba37aa03SKumar Gala do_fixup_by_path(fdt, path, "mac-address", &mac_addr, 6, 0); 503ab544633SKumar Gala do_fixup_by_path(fdt, path, "local-mac-address", 504ba37aa03SKumar Gala &mac_addr, 6, 1); 505ba37aa03SKumar Gala 506ba37aa03SKumar Gala sprintf(mac, "eth%daddr", ++i); 507ab544633SKumar Gala } 508ab544633SKumar Gala } 50918e69a35SAnton Vorontsov 5103082d234SKumar Gala /* Resize the fdt to its actual size + a bit of padding */ 5115bf58cccSSimon Glass int fdt_shrink_to_minimum(void *blob) 5123082d234SKumar Gala { 5133082d234SKumar Gala int i; 5143082d234SKumar Gala uint64_t addr, size; 5153082d234SKumar Gala int total, ret; 5163082d234SKumar Gala uint actualsize; 5173082d234SKumar Gala 5183082d234SKumar Gala if (!blob) 5193082d234SKumar Gala return 0; 5203082d234SKumar Gala 5213082d234SKumar Gala total = fdt_num_mem_rsv(blob); 5223082d234SKumar Gala for (i = 0; i < total; i++) { 5233082d234SKumar Gala fdt_get_mem_rsv(blob, i, &addr, &size); 52492549358SSimon Glass if (addr == (uintptr_t)blob) { 5253082d234SKumar Gala fdt_del_mem_rsv(blob, i); 5263082d234SKumar Gala break; 5273082d234SKumar Gala } 5283082d234SKumar Gala } 5293082d234SKumar Gala 530f242a088SPeter Korsgaard /* 531f242a088SPeter Korsgaard * Calculate the actual size of the fdt 5323840ebfaSFeng Wang * plus the size needed for 5 fdt_add_mem_rsv, one 5333840ebfaSFeng Wang * for the fdt itself and 4 for a possible initrd 5343840ebfaSFeng Wang * ((initrd-start + initrd-end) * 2 (name & value)) 535f242a088SPeter Korsgaard */ 5363082d234SKumar Gala actualsize = fdt_off_dt_strings(blob) + 5373840ebfaSFeng Wang fdt_size_dt_strings(blob) + 5 * sizeof(struct fdt_reserve_entry); 5383082d234SKumar Gala 5393082d234SKumar Gala /* Make it so the fdt ends on a page boundary */ 54092549358SSimon Glass actualsize = ALIGN(actualsize + ((uintptr_t)blob & 0xfff), 0x1000); 54192549358SSimon Glass actualsize = actualsize - ((uintptr_t)blob & 0xfff); 5423082d234SKumar Gala 5433082d234SKumar Gala /* Change the fdt header to reflect the correct size */ 5443082d234SKumar Gala fdt_set_totalsize(blob, actualsize); 5453082d234SKumar Gala 5463082d234SKumar Gala /* Add the new reservation */ 54792549358SSimon Glass ret = fdt_add_mem_rsv(blob, (uintptr_t)blob, actualsize); 5483082d234SKumar Gala if (ret < 0) 5493082d234SKumar Gala return ret; 5503082d234SKumar Gala 5513082d234SKumar Gala return actualsize; 5523082d234SKumar Gala } 5538ab451c4SKumar Gala 5548ab451c4SKumar Gala #ifdef CONFIG_PCI 555cfd700beSKumar Gala #define CONFIG_SYS_PCI_NR_INBOUND_WIN 4 5568ab451c4SKumar Gala 5578ab451c4SKumar Gala #define FDT_PCI_PREFETCH (0x40000000) 5588ab451c4SKumar Gala #define FDT_PCI_MEM32 (0x02000000) 5598ab451c4SKumar Gala #define FDT_PCI_IO (0x01000000) 5608ab451c4SKumar Gala #define FDT_PCI_MEM64 (0x03000000) 5618ab451c4SKumar Gala 5628ab451c4SKumar Gala int fdt_pci_dma_ranges(void *blob, int phb_off, struct pci_controller *hose) { 5638ab451c4SKumar Gala 5648ab451c4SKumar Gala int addrcell, sizecell, len, r; 5658ab451c4SKumar Gala u32 *dma_range; 5668ab451c4SKumar Gala /* sized based on pci addr cells, size-cells, & address-cells */ 5678ab451c4SKumar Gala u32 dma_ranges[(3 + 2 + 2) * CONFIG_SYS_PCI_NR_INBOUND_WIN]; 5688ab451c4SKumar Gala 5698ab451c4SKumar Gala addrcell = fdt_getprop_u32_default(blob, "/", "#address-cells", 1); 5708ab451c4SKumar Gala sizecell = fdt_getprop_u32_default(blob, "/", "#size-cells", 1); 5718ab451c4SKumar Gala 5728ab451c4SKumar Gala dma_range = &dma_ranges[0]; 5738ab451c4SKumar Gala for (r = 0; r < hose->region_count; r++) { 5748ab451c4SKumar Gala u64 bus_start, phys_start, size; 5758ab451c4SKumar Gala 576ff4e66e9SKumar Gala /* skip if !PCI_REGION_SYS_MEMORY */ 577ff4e66e9SKumar Gala if (!(hose->regions[r].flags & PCI_REGION_SYS_MEMORY)) 5788ab451c4SKumar Gala continue; 5798ab451c4SKumar Gala 5808ab451c4SKumar Gala bus_start = (u64)hose->regions[r].bus_start; 5818ab451c4SKumar Gala phys_start = (u64)hose->regions[r].phys_start; 5828ab451c4SKumar Gala size = (u64)hose->regions[r].size; 5838ab451c4SKumar Gala 5848ab451c4SKumar Gala dma_range[0] = 0; 585cfd700beSKumar Gala if (size >= 0x100000000ull) 5868ab451c4SKumar Gala dma_range[0] |= FDT_PCI_MEM64; 5878ab451c4SKumar Gala else 5888ab451c4SKumar Gala dma_range[0] |= FDT_PCI_MEM32; 5898ab451c4SKumar Gala if (hose->regions[r].flags & PCI_REGION_PREFETCH) 5908ab451c4SKumar Gala dma_range[0] |= FDT_PCI_PREFETCH; 5918ab451c4SKumar Gala #ifdef CONFIG_SYS_PCI_64BIT 5928ab451c4SKumar Gala dma_range[1] = bus_start >> 32; 5938ab451c4SKumar Gala #else 5948ab451c4SKumar Gala dma_range[1] = 0; 5958ab451c4SKumar Gala #endif 5968ab451c4SKumar Gala dma_range[2] = bus_start & 0xffffffff; 5978ab451c4SKumar Gala 5988ab451c4SKumar Gala if (addrcell == 2) { 5998ab451c4SKumar Gala dma_range[3] = phys_start >> 32; 6008ab451c4SKumar Gala dma_range[4] = phys_start & 0xffffffff; 6018ab451c4SKumar Gala } else { 6028ab451c4SKumar Gala dma_range[3] = phys_start & 0xffffffff; 6038ab451c4SKumar Gala } 6048ab451c4SKumar Gala 6058ab451c4SKumar Gala if (sizecell == 2) { 6068ab451c4SKumar Gala dma_range[3 + addrcell + 0] = size >> 32; 6078ab451c4SKumar Gala dma_range[3 + addrcell + 1] = size & 0xffffffff; 6088ab451c4SKumar Gala } else { 6098ab451c4SKumar Gala dma_range[3 + addrcell + 0] = size & 0xffffffff; 6108ab451c4SKumar Gala } 6118ab451c4SKumar Gala 6128ab451c4SKumar Gala dma_range += (3 + addrcell + sizecell); 6138ab451c4SKumar Gala } 6148ab451c4SKumar Gala 6158ab451c4SKumar Gala len = dma_range - &dma_ranges[0]; 6168ab451c4SKumar Gala if (len) 6178ab451c4SKumar Gala fdt_setprop(blob, phb_off, "dma-ranges", &dma_ranges[0], len*4); 6188ab451c4SKumar Gala 6198ab451c4SKumar Gala return 0; 6208ab451c4SKumar Gala } 6218ab451c4SKumar Gala #endif 62230d45c0dSStefan Roese 62330d45c0dSStefan Roese #ifdef CONFIG_FDT_FIXUP_NOR_FLASH_SIZE 62430d45c0dSStefan Roese /* 6258a805df1SStefan Roese * Provide a weak default function to return the flash bank size. 6268a805df1SStefan Roese * There might be multiple non-identical flash chips connected to one 6278a805df1SStefan Roese * chip-select, so we need to pass an index as well. 6288a805df1SStefan Roese */ 6298a805df1SStefan Roese u32 __flash_get_bank_size(int cs, int idx) 6308a805df1SStefan Roese { 6318a805df1SStefan Roese extern flash_info_t flash_info[]; 6328a805df1SStefan Roese 6338a805df1SStefan Roese /* 6348a805df1SStefan Roese * As default, a simple 1:1 mapping is provided. Boards with 6358a805df1SStefan Roese * a different mapping need to supply a board specific mapping 6368a805df1SStefan Roese * routine. 6378a805df1SStefan Roese */ 6388a805df1SStefan Roese return flash_info[cs].size; 6398a805df1SStefan Roese } 6408a805df1SStefan Roese u32 flash_get_bank_size(int cs, int idx) 6418a805df1SStefan Roese __attribute__((weak, alias("__flash_get_bank_size"))); 6428a805df1SStefan Roese 6438a805df1SStefan Roese /* 64430d45c0dSStefan Roese * This function can be used to update the size in the "reg" property 6458a805df1SStefan Roese * of all NOR FLASH device nodes. This is necessary for boards with 64630d45c0dSStefan Roese * non-fixed NOR FLASH sizes. 64730d45c0dSStefan Roese */ 6488a805df1SStefan Roese int fdt_fixup_nor_flash_size(void *blob) 64930d45c0dSStefan Roese { 65030d45c0dSStefan Roese char compat[][16] = { "cfi-flash", "jedec-flash" }; 65130d45c0dSStefan Roese int off; 65230d45c0dSStefan Roese int len; 65330d45c0dSStefan Roese struct fdt_property *prop; 6542778a014SStefan Roese u32 *reg, *reg2; 65530d45c0dSStefan Roese int i; 65630d45c0dSStefan Roese 65730d45c0dSStefan Roese for (i = 0; i < 2; i++) { 65830d45c0dSStefan Roese off = fdt_node_offset_by_compatible(blob, -1, compat[i]); 65930d45c0dSStefan Roese while (off != -FDT_ERR_NOTFOUND) { 6608a805df1SStefan Roese int idx; 6618a805df1SStefan Roese 66230d45c0dSStefan Roese /* 6638a805df1SStefan Roese * Found one compatible node, so fixup the size 6648a805df1SStefan Roese * int its reg properties 66530d45c0dSStefan Roese */ 66630d45c0dSStefan Roese prop = fdt_get_property_w(blob, off, "reg", &len); 66730d45c0dSStefan Roese if (prop) { 6688a805df1SStefan Roese int tuple_size = 3 * sizeof(reg); 66930d45c0dSStefan Roese 6708a805df1SStefan Roese /* 6718a805df1SStefan Roese * There might be multiple reg-tuples, 6728a805df1SStefan Roese * so loop through them all 6738a805df1SStefan Roese */ 6742778a014SStefan Roese reg = reg2 = (u32 *)&prop->data[0]; 6752778a014SStefan Roese for (idx = 0; idx < (len / tuple_size); idx++) { 6768a805df1SStefan Roese /* 6778a805df1SStefan Roese * Update size in reg property 6788a805df1SStefan Roese */ 6798a805df1SStefan Roese reg[2] = flash_get_bank_size(reg[0], 6808a805df1SStefan Roese idx); 6812778a014SStefan Roese 6822778a014SStefan Roese /* 6832778a014SStefan Roese * Point to next reg tuple 6842778a014SStefan Roese */ 6852778a014SStefan Roese reg += 3; 68630d45c0dSStefan Roese } 6872778a014SStefan Roese 6882778a014SStefan Roese fdt_setprop(blob, off, "reg", reg2, len); 68930d45c0dSStefan Roese } 69030d45c0dSStefan Roese 69130d45c0dSStefan Roese /* Move to next compatible node */ 69230d45c0dSStefan Roese off = fdt_node_offset_by_compatible(blob, off, 69330d45c0dSStefan Roese compat[i]); 69430d45c0dSStefan Roese } 69530d45c0dSStefan Roese } 69630d45c0dSStefan Roese 6978a805df1SStefan Roese return 0; 69830d45c0dSStefan Roese } 69930d45c0dSStefan Roese #endif 7003c950e2eSAnatolij Gustschin 701cb2707afSMatthew McClintock int fdt_increase_size(void *fdt, int add_len) 702cb2707afSMatthew McClintock { 703cb2707afSMatthew McClintock int newlen; 704cb2707afSMatthew McClintock 705cb2707afSMatthew McClintock newlen = fdt_totalsize(fdt) + add_len; 706cb2707afSMatthew McClintock 707cb2707afSMatthew McClintock /* Open in place with a new len */ 708cb2707afSMatthew McClintock return fdt_open_into(fdt, fdt, newlen); 709cb2707afSMatthew McClintock } 710cb2707afSMatthew McClintock 7113c950e2eSAnatolij Gustschin #ifdef CONFIG_FDT_FIXUP_PARTITIONS 7123c950e2eSAnatolij Gustschin #include <jffs2/load_kernel.h> 7133c950e2eSAnatolij Gustschin #include <mtd_node.h> 7143c950e2eSAnatolij Gustschin 7153c950e2eSAnatolij Gustschin struct reg_cell { 7163c950e2eSAnatolij Gustschin unsigned int r0; 7173c950e2eSAnatolij Gustschin unsigned int r1; 7183c950e2eSAnatolij Gustschin }; 7193c950e2eSAnatolij Gustschin 7203c950e2eSAnatolij Gustschin int fdt_del_subnodes(const void *blob, int parent_offset) 7213c950e2eSAnatolij Gustschin { 7223c950e2eSAnatolij Gustschin int off, ndepth; 7233c950e2eSAnatolij Gustschin int ret; 7243c950e2eSAnatolij Gustschin 7253c950e2eSAnatolij Gustschin for (ndepth = 0, off = fdt_next_node(blob, parent_offset, &ndepth); 7263c950e2eSAnatolij Gustschin (off >= 0) && (ndepth > 0); 7273c950e2eSAnatolij Gustschin off = fdt_next_node(blob, off, &ndepth)) { 7283c950e2eSAnatolij Gustschin if (ndepth == 1) { 7293c950e2eSAnatolij Gustschin debug("delete %s: offset: %x\n", 7303c950e2eSAnatolij Gustschin fdt_get_name(blob, off, 0), off); 7313c950e2eSAnatolij Gustschin ret = fdt_del_node((void *)blob, off); 7323c950e2eSAnatolij Gustschin if (ret < 0) { 7333c950e2eSAnatolij Gustschin printf("Can't delete node: %s\n", 7343c950e2eSAnatolij Gustschin fdt_strerror(ret)); 7353c950e2eSAnatolij Gustschin return ret; 7363c950e2eSAnatolij Gustschin } else { 7373c950e2eSAnatolij Gustschin ndepth = 0; 7383c950e2eSAnatolij Gustschin off = parent_offset; 7393c950e2eSAnatolij Gustschin } 7403c950e2eSAnatolij Gustschin } 7413c950e2eSAnatolij Gustschin } 7423c950e2eSAnatolij Gustschin return 0; 7433c950e2eSAnatolij Gustschin } 7443c950e2eSAnatolij Gustschin 7453c950e2eSAnatolij Gustschin int fdt_del_partitions(void *blob, int parent_offset) 7463c950e2eSAnatolij Gustschin { 7473c950e2eSAnatolij Gustschin const void *prop; 7483c950e2eSAnatolij Gustschin int ndepth = 0; 7493c950e2eSAnatolij Gustschin int off; 7503c950e2eSAnatolij Gustschin int ret; 7513c950e2eSAnatolij Gustschin 7523c950e2eSAnatolij Gustschin off = fdt_next_node(blob, parent_offset, &ndepth); 7533c950e2eSAnatolij Gustschin if (off > 0 && ndepth == 1) { 7543c950e2eSAnatolij Gustschin prop = fdt_getprop(blob, off, "label", NULL); 7553c950e2eSAnatolij Gustschin if (prop == NULL) { 7563c950e2eSAnatolij Gustschin /* 7573c950e2eSAnatolij Gustschin * Could not find label property, nand {}; node? 7583c950e2eSAnatolij Gustschin * Check subnode, delete partitions there if any. 7593c950e2eSAnatolij Gustschin */ 7603c950e2eSAnatolij Gustschin return fdt_del_partitions(blob, off); 7613c950e2eSAnatolij Gustschin } else { 7623c950e2eSAnatolij Gustschin ret = fdt_del_subnodes(blob, parent_offset); 7633c950e2eSAnatolij Gustschin if (ret < 0) { 7643c950e2eSAnatolij Gustschin printf("Can't remove subnodes: %s\n", 7653c950e2eSAnatolij Gustschin fdt_strerror(ret)); 7663c950e2eSAnatolij Gustschin return ret; 7673c950e2eSAnatolij Gustschin } 7683c950e2eSAnatolij Gustschin } 7693c950e2eSAnatolij Gustschin } 7703c950e2eSAnatolij Gustschin return 0; 7713c950e2eSAnatolij Gustschin } 7723c950e2eSAnatolij Gustschin 7733c950e2eSAnatolij Gustschin int fdt_node_set_part_info(void *blob, int parent_offset, 7743c950e2eSAnatolij Gustschin struct mtd_device *dev) 7753c950e2eSAnatolij Gustschin { 7763c950e2eSAnatolij Gustschin struct list_head *pentry; 7773c950e2eSAnatolij Gustschin struct part_info *part; 7783c950e2eSAnatolij Gustschin struct reg_cell cell; 7793c950e2eSAnatolij Gustschin int off, ndepth = 0; 7803c950e2eSAnatolij Gustschin int part_num, ret; 7813c950e2eSAnatolij Gustschin char buf[64]; 7823c950e2eSAnatolij Gustschin 7833c950e2eSAnatolij Gustschin ret = fdt_del_partitions(blob, parent_offset); 7843c950e2eSAnatolij Gustschin if (ret < 0) 7853c950e2eSAnatolij Gustschin return ret; 7863c950e2eSAnatolij Gustschin 7873c950e2eSAnatolij Gustschin /* 7883c950e2eSAnatolij Gustschin * Check if it is nand {}; subnode, adjust 7893c950e2eSAnatolij Gustschin * the offset in this case 7903c950e2eSAnatolij Gustschin */ 7913c950e2eSAnatolij Gustschin off = fdt_next_node(blob, parent_offset, &ndepth); 7923c950e2eSAnatolij Gustschin if (off > 0 && ndepth == 1) 7933c950e2eSAnatolij Gustschin parent_offset = off; 7943c950e2eSAnatolij Gustschin 7953c950e2eSAnatolij Gustschin part_num = 0; 7963c950e2eSAnatolij Gustschin list_for_each_prev(pentry, &dev->parts) { 7973c950e2eSAnatolij Gustschin int newoff; 7983c950e2eSAnatolij Gustschin 7993c950e2eSAnatolij Gustschin part = list_entry(pentry, struct part_info, link); 8003c950e2eSAnatolij Gustschin 80106503f16SScott Wood debug("%2d: %-20s0x%08llx\t0x%08llx\t%d\n", 8023c950e2eSAnatolij Gustschin part_num, part->name, part->size, 8033c950e2eSAnatolij Gustschin part->offset, part->mask_flags); 8043c950e2eSAnatolij Gustschin 80506503f16SScott Wood sprintf(buf, "partition@%llx", part->offset); 8063c950e2eSAnatolij Gustschin add_sub: 8073c950e2eSAnatolij Gustschin ret = fdt_add_subnode(blob, parent_offset, buf); 8083c950e2eSAnatolij Gustschin if (ret == -FDT_ERR_NOSPACE) { 8093c950e2eSAnatolij Gustschin ret = fdt_increase_size(blob, 512); 8103c950e2eSAnatolij Gustschin if (!ret) 8113c950e2eSAnatolij Gustschin goto add_sub; 8123c950e2eSAnatolij Gustschin else 8133c950e2eSAnatolij Gustschin goto err_size; 8143c950e2eSAnatolij Gustschin } else if (ret < 0) { 8153c950e2eSAnatolij Gustschin printf("Can't add partition node: %s\n", 8163c950e2eSAnatolij Gustschin fdt_strerror(ret)); 8173c950e2eSAnatolij Gustschin return ret; 8183c950e2eSAnatolij Gustschin } 8193c950e2eSAnatolij Gustschin newoff = ret; 8203c950e2eSAnatolij Gustschin 8213c950e2eSAnatolij Gustschin /* Check MTD_WRITEABLE_CMD flag */ 8223c950e2eSAnatolij Gustschin if (part->mask_flags & 1) { 8233c950e2eSAnatolij Gustschin add_ro: 8243c950e2eSAnatolij Gustschin ret = fdt_setprop(blob, newoff, "read_only", NULL, 0); 8253c950e2eSAnatolij Gustschin if (ret == -FDT_ERR_NOSPACE) { 8263c950e2eSAnatolij Gustschin ret = fdt_increase_size(blob, 512); 8273c950e2eSAnatolij Gustschin if (!ret) 8283c950e2eSAnatolij Gustschin goto add_ro; 8293c950e2eSAnatolij Gustschin else 8303c950e2eSAnatolij Gustschin goto err_size; 8313c950e2eSAnatolij Gustschin } else if (ret < 0) 8323c950e2eSAnatolij Gustschin goto err_prop; 8333c950e2eSAnatolij Gustschin } 8343c950e2eSAnatolij Gustschin 8353c950e2eSAnatolij Gustschin cell.r0 = cpu_to_fdt32(part->offset); 8363c950e2eSAnatolij Gustschin cell.r1 = cpu_to_fdt32(part->size); 8373c950e2eSAnatolij Gustschin add_reg: 8383c950e2eSAnatolij Gustschin ret = fdt_setprop(blob, newoff, "reg", &cell, sizeof(cell)); 8393c950e2eSAnatolij Gustschin if (ret == -FDT_ERR_NOSPACE) { 8403c950e2eSAnatolij Gustschin ret = fdt_increase_size(blob, 512); 8413c950e2eSAnatolij Gustschin if (!ret) 8423c950e2eSAnatolij Gustschin goto add_reg; 8433c950e2eSAnatolij Gustschin else 8443c950e2eSAnatolij Gustschin goto err_size; 8453c950e2eSAnatolij Gustschin } else if (ret < 0) 8463c950e2eSAnatolij Gustschin goto err_prop; 8473c950e2eSAnatolij Gustschin 8483c950e2eSAnatolij Gustschin add_label: 8493c950e2eSAnatolij Gustschin ret = fdt_setprop_string(blob, newoff, "label", part->name); 8503c950e2eSAnatolij Gustschin if (ret == -FDT_ERR_NOSPACE) { 8513c950e2eSAnatolij Gustschin ret = fdt_increase_size(blob, 512); 8523c950e2eSAnatolij Gustschin if (!ret) 8533c950e2eSAnatolij Gustschin goto add_label; 8543c950e2eSAnatolij Gustschin else 8553c950e2eSAnatolij Gustschin goto err_size; 8563c950e2eSAnatolij Gustschin } else if (ret < 0) 8573c950e2eSAnatolij Gustschin goto err_prop; 8583c950e2eSAnatolij Gustschin 8593c950e2eSAnatolij Gustschin part_num++; 8603c950e2eSAnatolij Gustschin } 8613c950e2eSAnatolij Gustschin return 0; 8623c950e2eSAnatolij Gustschin err_size: 8633c950e2eSAnatolij Gustschin printf("Can't increase blob size: %s\n", fdt_strerror(ret)); 8643c950e2eSAnatolij Gustschin return ret; 8653c950e2eSAnatolij Gustschin err_prop: 8663c950e2eSAnatolij Gustschin printf("Can't add property: %s\n", fdt_strerror(ret)); 8673c950e2eSAnatolij Gustschin return ret; 8683c950e2eSAnatolij Gustschin } 8693c950e2eSAnatolij Gustschin 8703c950e2eSAnatolij Gustschin /* 8713c950e2eSAnatolij Gustschin * Update partitions in nor/nand nodes using info from 8723c950e2eSAnatolij Gustschin * mtdparts environment variable. The nodes to update are 8733c950e2eSAnatolij Gustschin * specified by node_info structure which contains mtd device 8743c950e2eSAnatolij Gustschin * type and compatible string: E. g. the board code in 8753c950e2eSAnatolij Gustschin * ft_board_setup() could use: 8763c950e2eSAnatolij Gustschin * 8773c950e2eSAnatolij Gustschin * struct node_info nodes[] = { 8783c950e2eSAnatolij Gustschin * { "fsl,mpc5121-nfc", MTD_DEV_TYPE_NAND, }, 8793c950e2eSAnatolij Gustschin * { "cfi-flash", MTD_DEV_TYPE_NOR, }, 8803c950e2eSAnatolij Gustschin * }; 8813c950e2eSAnatolij Gustschin * 8823c950e2eSAnatolij Gustschin * fdt_fixup_mtdparts(blob, nodes, ARRAY_SIZE(nodes)); 8833c950e2eSAnatolij Gustschin */ 8843c950e2eSAnatolij Gustschin void fdt_fixup_mtdparts(void *blob, void *node_info, int node_info_size) 8853c950e2eSAnatolij Gustschin { 8863c950e2eSAnatolij Gustschin struct node_info *ni = node_info; 8873c950e2eSAnatolij Gustschin struct mtd_device *dev; 8883c950e2eSAnatolij Gustschin char *parts; 8893c950e2eSAnatolij Gustschin int i, idx; 8903c950e2eSAnatolij Gustschin int noff; 8913c950e2eSAnatolij Gustschin 8923c950e2eSAnatolij Gustschin parts = getenv("mtdparts"); 8933c950e2eSAnatolij Gustschin if (!parts) 8943c950e2eSAnatolij Gustschin return; 8953c950e2eSAnatolij Gustschin 8963c950e2eSAnatolij Gustschin if (mtdparts_init() != 0) 8973c950e2eSAnatolij Gustschin return; 8983c950e2eSAnatolij Gustschin 8993c950e2eSAnatolij Gustschin for (i = 0; i < node_info_size; i++) { 9003c950e2eSAnatolij Gustschin idx = 0; 9013c950e2eSAnatolij Gustschin noff = fdt_node_offset_by_compatible(blob, -1, ni[i].compat); 9023c950e2eSAnatolij Gustschin while (noff != -FDT_ERR_NOTFOUND) { 9033c950e2eSAnatolij Gustschin debug("%s: %s, mtd dev type %d\n", 9043c950e2eSAnatolij Gustschin fdt_get_name(blob, noff, 0), 9053c950e2eSAnatolij Gustschin ni[i].compat, ni[i].type); 9063c950e2eSAnatolij Gustschin dev = device_find(ni[i].type, idx++); 9073c950e2eSAnatolij Gustschin if (dev) { 9083c950e2eSAnatolij Gustschin if (fdt_node_set_part_info(blob, noff, dev)) 9093c950e2eSAnatolij Gustschin return; /* return on error */ 9103c950e2eSAnatolij Gustschin } 9113c950e2eSAnatolij Gustschin 9123c950e2eSAnatolij Gustschin /* Jump to next flash node */ 9133c950e2eSAnatolij Gustschin noff = fdt_node_offset_by_compatible(blob, noff, 9143c950e2eSAnatolij Gustschin ni[i].compat); 9153c950e2eSAnatolij Gustschin } 9163c950e2eSAnatolij Gustschin } 9173c950e2eSAnatolij Gustschin } 9183c950e2eSAnatolij Gustschin #endif 91949b97d9cSKumar Gala 92049b97d9cSKumar Gala void fdt_del_node_and_alias(void *blob, const char *alias) 92149b97d9cSKumar Gala { 92249b97d9cSKumar Gala int off = fdt_path_offset(blob, alias); 92349b97d9cSKumar Gala 92449b97d9cSKumar Gala if (off < 0) 92549b97d9cSKumar Gala return; 92649b97d9cSKumar Gala 92749b97d9cSKumar Gala fdt_del_node(blob, off); 92849b97d9cSKumar Gala 92949b97d9cSKumar Gala off = fdt_path_offset(blob, "/aliases"); 93049b97d9cSKumar Gala fdt_delprop(blob, off, alias); 93149b97d9cSKumar Gala } 932a0342c08SKumar Gala 933a0342c08SKumar Gala #define PRu64 "%llx" 934a0342c08SKumar Gala 935a0342c08SKumar Gala /* Max address size we deal with */ 936a0342c08SKumar Gala #define OF_MAX_ADDR_CELLS 4 937a0342c08SKumar Gala #define OF_BAD_ADDR ((u64)-1) 938a0342c08SKumar Gala #define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \ 939a0342c08SKumar Gala (ns) > 0) 940a0342c08SKumar Gala 941a0342c08SKumar Gala /* Debug utility */ 942a0342c08SKumar Gala #ifdef DEBUG 9438aa5ec6eSKim Phillips static void of_dump_addr(const char *s, const fdt32_t *addr, int na) 944a0342c08SKumar Gala { 945a0342c08SKumar Gala printf("%s", s); 946a0342c08SKumar Gala while(na--) 947a0342c08SKumar Gala printf(" %08x", *(addr++)); 948a0342c08SKumar Gala printf("\n"); 949a0342c08SKumar Gala } 950a0342c08SKumar Gala #else 9518aa5ec6eSKim Phillips static void of_dump_addr(const char *s, const fdt32_t *addr, int na) { } 952a0342c08SKumar Gala #endif 953a0342c08SKumar Gala 954a0342c08SKumar Gala /* Callbacks for bus specific translators */ 955a0342c08SKumar Gala struct of_bus { 956a0342c08SKumar Gala const char *name; 957a0342c08SKumar Gala const char *addresses; 9586395f318SScott Wood void (*count_cells)(void *blob, int parentoffset, 959a0342c08SKumar Gala int *addrc, int *sizec); 9608aa5ec6eSKim Phillips u64 (*map)(fdt32_t *addr, const fdt32_t *range, 961a0342c08SKumar Gala int na, int ns, int pna); 9628aa5ec6eSKim Phillips int (*translate)(fdt32_t *addr, u64 offset, int na); 963a0342c08SKumar Gala }; 964a0342c08SKumar Gala 965a0342c08SKumar Gala /* Default translator (generic bus) */ 966f43b4356SArnab Basu void of_bus_default_count_cells(void *blob, int parentoffset, 967a0342c08SKumar Gala int *addrc, int *sizec) 968a0342c08SKumar Gala { 9698aa5ec6eSKim Phillips const fdt32_t *prop; 9706395f318SScott Wood 9716395f318SScott Wood if (addrc) { 9726395f318SScott Wood prop = fdt_getprop(blob, parentoffset, "#address-cells", NULL); 9736395f318SScott Wood if (prop) 9748aa5ec6eSKim Phillips *addrc = be32_to_cpup(prop); 9756395f318SScott Wood else 9766395f318SScott Wood *addrc = 2; 9776395f318SScott Wood } 9786395f318SScott Wood 9796395f318SScott Wood if (sizec) { 9806395f318SScott Wood prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL); 9816395f318SScott Wood if (prop) 9828aa5ec6eSKim Phillips *sizec = be32_to_cpup(prop); 9836395f318SScott Wood else 9846395f318SScott Wood *sizec = 1; 9856395f318SScott Wood } 986a0342c08SKumar Gala } 987a0342c08SKumar Gala 9888aa5ec6eSKim Phillips static u64 of_bus_default_map(fdt32_t *addr, const fdt32_t *range, 989a0342c08SKumar Gala int na, int ns, int pna) 990a0342c08SKumar Gala { 991a0342c08SKumar Gala u64 cp, s, da; 992a0342c08SKumar Gala 993a0342c08SKumar Gala cp = of_read_number(range, na); 994a0342c08SKumar Gala s = of_read_number(range + na + pna, ns); 995a0342c08SKumar Gala da = of_read_number(addr, na); 996a0342c08SKumar Gala 997a0342c08SKumar Gala debug("OF: default map, cp="PRu64", s="PRu64", da="PRu64"\n", 998a0342c08SKumar Gala cp, s, da); 999a0342c08SKumar Gala 1000a0342c08SKumar Gala if (da < cp || da >= (cp + s)) 1001a0342c08SKumar Gala return OF_BAD_ADDR; 1002a0342c08SKumar Gala return da - cp; 1003a0342c08SKumar Gala } 1004a0342c08SKumar Gala 10058aa5ec6eSKim Phillips static int of_bus_default_translate(fdt32_t *addr, u64 offset, int na) 1006a0342c08SKumar Gala { 1007a0342c08SKumar Gala u64 a = of_read_number(addr, na); 1008a0342c08SKumar Gala memset(addr, 0, na * 4); 1009a0342c08SKumar Gala a += offset; 1010a0342c08SKumar Gala if (na > 1) 10118aa5ec6eSKim Phillips addr[na - 2] = cpu_to_fdt32(a >> 32); 10128aa5ec6eSKim Phillips addr[na - 1] = cpu_to_fdt32(a & 0xffffffffu); 1013a0342c08SKumar Gala 1014a0342c08SKumar Gala return 0; 1015a0342c08SKumar Gala } 1016a0342c08SKumar Gala 1017a0342c08SKumar Gala /* Array of bus specific translators */ 1018a0342c08SKumar Gala static struct of_bus of_busses[] = { 1019a0342c08SKumar Gala /* Default */ 1020a0342c08SKumar Gala { 1021a0342c08SKumar Gala .name = "default", 1022a0342c08SKumar Gala .addresses = "reg", 1023a0342c08SKumar Gala .count_cells = of_bus_default_count_cells, 1024a0342c08SKumar Gala .map = of_bus_default_map, 1025a0342c08SKumar Gala .translate = of_bus_default_translate, 1026a0342c08SKumar Gala }, 1027a0342c08SKumar Gala }; 1028a0342c08SKumar Gala 1029a0342c08SKumar Gala static int of_translate_one(void * blob, int parent, struct of_bus *bus, 10308aa5ec6eSKim Phillips struct of_bus *pbus, fdt32_t *addr, 1031a0342c08SKumar Gala int na, int ns, int pna, const char *rprop) 1032a0342c08SKumar Gala { 10338aa5ec6eSKim Phillips const fdt32_t *ranges; 1034a0342c08SKumar Gala int rlen; 1035a0342c08SKumar Gala int rone; 1036a0342c08SKumar Gala u64 offset = OF_BAD_ADDR; 1037a0342c08SKumar Gala 1038a0342c08SKumar Gala /* Normally, an absence of a "ranges" property means we are 1039a0342c08SKumar Gala * crossing a non-translatable boundary, and thus the addresses 1040a0342c08SKumar Gala * below the current not cannot be converted to CPU physical ones. 1041a0342c08SKumar Gala * Unfortunately, while this is very clear in the spec, it's not 1042a0342c08SKumar Gala * what Apple understood, and they do have things like /uni-n or 1043a0342c08SKumar Gala * /ht nodes with no "ranges" property and a lot of perfectly 1044a0342c08SKumar Gala * useable mapped devices below them. Thus we treat the absence of 1045a0342c08SKumar Gala * "ranges" as equivalent to an empty "ranges" property which means 1046a0342c08SKumar Gala * a 1:1 translation at that level. It's up to the caller not to try 1047a0342c08SKumar Gala * to translate addresses that aren't supposed to be translated in 1048a0342c08SKumar Gala * the first place. --BenH. 1049a0342c08SKumar Gala */ 10508aa5ec6eSKim Phillips ranges = fdt_getprop(blob, parent, rprop, &rlen); 1051a0342c08SKumar Gala if (ranges == NULL || rlen == 0) { 1052a0342c08SKumar Gala offset = of_read_number(addr, na); 1053a0342c08SKumar Gala memset(addr, 0, pna * 4); 1054a0342c08SKumar Gala debug("OF: no ranges, 1:1 translation\n"); 1055a0342c08SKumar Gala goto finish; 1056a0342c08SKumar Gala } 1057a0342c08SKumar Gala 1058a0342c08SKumar Gala debug("OF: walking ranges...\n"); 1059a0342c08SKumar Gala 1060a0342c08SKumar Gala /* Now walk through the ranges */ 1061a0342c08SKumar Gala rlen /= 4; 1062a0342c08SKumar Gala rone = na + pna + ns; 1063a0342c08SKumar Gala for (; rlen >= rone; rlen -= rone, ranges += rone) { 1064a0342c08SKumar Gala offset = bus->map(addr, ranges, na, ns, pna); 1065a0342c08SKumar Gala if (offset != OF_BAD_ADDR) 1066a0342c08SKumar Gala break; 1067a0342c08SKumar Gala } 1068a0342c08SKumar Gala if (offset == OF_BAD_ADDR) { 1069a0342c08SKumar Gala debug("OF: not found !\n"); 1070a0342c08SKumar Gala return 1; 1071a0342c08SKumar Gala } 1072a0342c08SKumar Gala memcpy(addr, ranges + na, 4 * pna); 1073a0342c08SKumar Gala 1074a0342c08SKumar Gala finish: 1075a0342c08SKumar Gala of_dump_addr("OF: parent translation for:", addr, pna); 1076a0342c08SKumar Gala debug("OF: with offset: "PRu64"\n", offset); 1077a0342c08SKumar Gala 1078a0342c08SKumar Gala /* Translate it into parent bus space */ 1079a0342c08SKumar Gala return pbus->translate(addr, offset, pna); 1080a0342c08SKumar Gala } 1081a0342c08SKumar Gala 1082a0342c08SKumar Gala /* 1083a0342c08SKumar Gala * Translate an address from the device-tree into a CPU physical address, 1084a0342c08SKumar Gala * this walks up the tree and applies the various bus mappings on the 1085a0342c08SKumar Gala * way. 1086a0342c08SKumar Gala * 1087a0342c08SKumar Gala * Note: We consider that crossing any level with #size-cells == 0 to mean 1088a0342c08SKumar Gala * that translation is impossible (that is we are not dealing with a value 1089a0342c08SKumar Gala * that can be mapped to a cpu physical address). This is not really specified 1090a0342c08SKumar Gala * that way, but this is traditionally the way IBM at least do things 1091a0342c08SKumar Gala */ 10928aa5ec6eSKim Phillips static u64 __of_translate_address(void *blob, int node_offset, const fdt32_t *in_addr, 1093a0342c08SKumar Gala const char *rprop) 1094a0342c08SKumar Gala { 1095a0342c08SKumar Gala int parent; 1096a0342c08SKumar Gala struct of_bus *bus, *pbus; 10978aa5ec6eSKim Phillips fdt32_t addr[OF_MAX_ADDR_CELLS]; 1098a0342c08SKumar Gala int na, ns, pna, pns; 1099a0342c08SKumar Gala u64 result = OF_BAD_ADDR; 1100a0342c08SKumar Gala 1101a0342c08SKumar Gala debug("OF: ** translation for device %s **\n", 1102a0342c08SKumar Gala fdt_get_name(blob, node_offset, NULL)); 1103a0342c08SKumar Gala 1104a0342c08SKumar Gala /* Get parent & match bus type */ 1105a0342c08SKumar Gala parent = fdt_parent_offset(blob, node_offset); 1106a0342c08SKumar Gala if (parent < 0) 1107a0342c08SKumar Gala goto bail; 1108a0342c08SKumar Gala bus = &of_busses[0]; 1109a0342c08SKumar Gala 1110a0342c08SKumar Gala /* Cound address cells & copy address locally */ 11116395f318SScott Wood bus->count_cells(blob, parent, &na, &ns); 1112a0342c08SKumar Gala if (!OF_CHECK_COUNTS(na, ns)) { 1113a0342c08SKumar Gala printf("%s: Bad cell count for %s\n", __FUNCTION__, 1114a0342c08SKumar Gala fdt_get_name(blob, node_offset, NULL)); 1115a0342c08SKumar Gala goto bail; 1116a0342c08SKumar Gala } 1117a0342c08SKumar Gala memcpy(addr, in_addr, na * 4); 1118a0342c08SKumar Gala 1119a0342c08SKumar Gala debug("OF: bus is %s (na=%d, ns=%d) on %s\n", 1120a0342c08SKumar Gala bus->name, na, ns, fdt_get_name(blob, parent, NULL)); 1121a0342c08SKumar Gala of_dump_addr("OF: translating address:", addr, na); 1122a0342c08SKumar Gala 1123a0342c08SKumar Gala /* Translate */ 1124a0342c08SKumar Gala for (;;) { 1125a0342c08SKumar Gala /* Switch to parent bus */ 1126a0342c08SKumar Gala node_offset = parent; 1127a0342c08SKumar Gala parent = fdt_parent_offset(blob, node_offset); 1128a0342c08SKumar Gala 1129a0342c08SKumar Gala /* If root, we have finished */ 1130a0342c08SKumar Gala if (parent < 0) { 1131a0342c08SKumar Gala debug("OF: reached root node\n"); 1132a0342c08SKumar Gala result = of_read_number(addr, na); 1133a0342c08SKumar Gala break; 1134a0342c08SKumar Gala } 1135a0342c08SKumar Gala 1136a0342c08SKumar Gala /* Get new parent bus and counts */ 1137a0342c08SKumar Gala pbus = &of_busses[0]; 11386395f318SScott Wood pbus->count_cells(blob, parent, &pna, &pns); 1139a0342c08SKumar Gala if (!OF_CHECK_COUNTS(pna, pns)) { 1140a0342c08SKumar Gala printf("%s: Bad cell count for %s\n", __FUNCTION__, 1141a0342c08SKumar Gala fdt_get_name(blob, node_offset, NULL)); 1142a0342c08SKumar Gala break; 1143a0342c08SKumar Gala } 1144a0342c08SKumar Gala 1145a0342c08SKumar Gala debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n", 1146a0342c08SKumar Gala pbus->name, pna, pns, fdt_get_name(blob, parent, NULL)); 1147a0342c08SKumar Gala 1148a0342c08SKumar Gala /* Apply bus translation */ 1149a0342c08SKumar Gala if (of_translate_one(blob, node_offset, bus, pbus, 1150a0342c08SKumar Gala addr, na, ns, pna, rprop)) 1151a0342c08SKumar Gala break; 1152a0342c08SKumar Gala 1153a0342c08SKumar Gala /* Complete the move up one level */ 1154a0342c08SKumar Gala na = pna; 1155a0342c08SKumar Gala ns = pns; 1156a0342c08SKumar Gala bus = pbus; 1157a0342c08SKumar Gala 1158a0342c08SKumar Gala of_dump_addr("OF: one level translation:", addr, na); 1159a0342c08SKumar Gala } 1160a0342c08SKumar Gala bail: 1161a0342c08SKumar Gala 1162a0342c08SKumar Gala return result; 1163a0342c08SKumar Gala } 1164a0342c08SKumar Gala 11658aa5ec6eSKim Phillips u64 fdt_translate_address(void *blob, int node_offset, const fdt32_t *in_addr) 1166a0342c08SKumar Gala { 1167a0342c08SKumar Gala return __of_translate_address(blob, node_offset, in_addr, "ranges"); 1168a0342c08SKumar Gala } 116975e73afdSKumar Gala 117075e73afdSKumar Gala /** 117175e73afdSKumar Gala * fdt_node_offset_by_compat_reg: Find a node that matches compatiable and 117275e73afdSKumar Gala * who's reg property matches a physical cpu address 117375e73afdSKumar Gala * 117475e73afdSKumar Gala * @blob: ptr to device tree 117575e73afdSKumar Gala * @compat: compatiable string to match 117675e73afdSKumar Gala * @compat_off: property name 117775e73afdSKumar Gala * 117875e73afdSKumar Gala */ 117975e73afdSKumar Gala int fdt_node_offset_by_compat_reg(void *blob, const char *compat, 118075e73afdSKumar Gala phys_addr_t compat_off) 118175e73afdSKumar Gala { 118275e73afdSKumar Gala int len, off = fdt_node_offset_by_compatible(blob, -1, compat); 118375e73afdSKumar Gala while (off != -FDT_ERR_NOTFOUND) { 11848aa5ec6eSKim Phillips const fdt32_t *reg = fdt_getprop(blob, off, "reg", &len); 118575e73afdSKumar Gala if (reg) { 118675e73afdSKumar Gala if (compat_off == fdt_translate_address(blob, off, reg)) 118775e73afdSKumar Gala return off; 118875e73afdSKumar Gala } 118975e73afdSKumar Gala off = fdt_node_offset_by_compatible(blob, off, compat); 119075e73afdSKumar Gala } 119175e73afdSKumar Gala 119275e73afdSKumar Gala return -FDT_ERR_NOTFOUND; 119375e73afdSKumar Gala } 119475e73afdSKumar Gala 1195b4b847e9SKumar Gala /** 1196b4b847e9SKumar Gala * fdt_alloc_phandle: Return next free phandle value 1197b4b847e9SKumar Gala * 1198b4b847e9SKumar Gala * @blob: ptr to device tree 1199b4b847e9SKumar Gala */ 1200b4b847e9SKumar Gala int fdt_alloc_phandle(void *blob) 1201b4b847e9SKumar Gala { 120250bf17bdSTimur Tabi int offset, phandle = 0; 120375e73afdSKumar Gala 1204b4b847e9SKumar Gala for (offset = fdt_next_node(blob, -1, NULL); offset >= 0; 1205b4b847e9SKumar Gala offset = fdt_next_node(blob, offset, NULL)) { 120650bf17bdSTimur Tabi phandle = max(phandle, fdt_get_phandle(blob, offset)); 1207b4b847e9SKumar Gala } 1208b4b847e9SKumar Gala 1209b4b847e9SKumar Gala return phandle + 1; 1210b4b847e9SKumar Gala } 1211beca5a5fSAnatolij Gustschin 1212a8d2a75dSGerald Van Baren /* 1213f117c0f0SKumar Gala * fdt_set_phandle: Create a phandle property for the given node 1214a8d2a75dSGerald Van Baren * 1215a8d2a75dSGerald Van Baren * @fdt: ptr to device tree 1216a8d2a75dSGerald Van Baren * @nodeoffset: node to update 1217a8d2a75dSGerald Van Baren * @phandle: phandle value to set (must be unique) 1218a8d2a75dSGerald Van Baren */ 1219f117c0f0SKumar Gala int fdt_set_phandle(void *fdt, int nodeoffset, uint32_t phandle) 1220a8d2a75dSGerald Van Baren { 1221a8d2a75dSGerald Van Baren int ret; 1222a8d2a75dSGerald Van Baren 1223a8d2a75dSGerald Van Baren #ifdef DEBUG 1224a8d2a75dSGerald Van Baren int off = fdt_node_offset_by_phandle(fdt, phandle); 1225a8d2a75dSGerald Van Baren 1226a8d2a75dSGerald Van Baren if ((off >= 0) && (off != nodeoffset)) { 1227a8d2a75dSGerald Van Baren char buf[64]; 1228a8d2a75dSGerald Van Baren 1229a8d2a75dSGerald Van Baren fdt_get_path(fdt, nodeoffset, buf, sizeof(buf)); 1230a8d2a75dSGerald Van Baren printf("Trying to update node %s with phandle %u ", 1231a8d2a75dSGerald Van Baren buf, phandle); 1232a8d2a75dSGerald Van Baren 1233a8d2a75dSGerald Van Baren fdt_get_path(fdt, off, buf, sizeof(buf)); 1234a8d2a75dSGerald Van Baren printf("that already exists in node %s.\n", buf); 1235a8d2a75dSGerald Van Baren return -FDT_ERR_BADPHANDLE; 1236a8d2a75dSGerald Van Baren } 1237a8d2a75dSGerald Van Baren #endif 1238a8d2a75dSGerald Van Baren 1239a8d2a75dSGerald Van Baren ret = fdt_setprop_cell(fdt, nodeoffset, "phandle", phandle); 1240a8d2a75dSGerald Van Baren if (ret < 0) 1241a8d2a75dSGerald Van Baren return ret; 1242a8d2a75dSGerald Van Baren 1243a8d2a75dSGerald Van Baren /* 1244a8d2a75dSGerald Van Baren * For now, also set the deprecated "linux,phandle" property, so that we 1245a8d2a75dSGerald Van Baren * don't break older kernels. 1246a8d2a75dSGerald Van Baren */ 1247a8d2a75dSGerald Van Baren ret = fdt_setprop_cell(fdt, nodeoffset, "linux,phandle", phandle); 1248a8d2a75dSGerald Van Baren 1249a8d2a75dSGerald Van Baren return ret; 1250a8d2a75dSGerald Van Baren } 1251a8d2a75dSGerald Van Baren 125210aeabd1SKumar Gala /* 125310aeabd1SKumar Gala * fdt_create_phandle: Create a phandle property for the given node 125410aeabd1SKumar Gala * 125510aeabd1SKumar Gala * @fdt: ptr to device tree 125610aeabd1SKumar Gala * @nodeoffset: node to update 125710aeabd1SKumar Gala */ 12583c927cccSTimur Tabi unsigned int fdt_create_phandle(void *fdt, int nodeoffset) 125910aeabd1SKumar Gala { 126010aeabd1SKumar Gala /* see if there is a phandle already */ 126110aeabd1SKumar Gala int phandle = fdt_get_phandle(fdt, nodeoffset); 126210aeabd1SKumar Gala 126310aeabd1SKumar Gala /* if we got 0, means no phandle so create one */ 126410aeabd1SKumar Gala if (phandle == 0) { 12653c927cccSTimur Tabi int ret; 12663c927cccSTimur Tabi 126710aeabd1SKumar Gala phandle = fdt_alloc_phandle(fdt); 12683c927cccSTimur Tabi ret = fdt_set_phandle(fdt, nodeoffset, phandle); 12693c927cccSTimur Tabi if (ret < 0) { 12703c927cccSTimur Tabi printf("Can't set phandle %u: %s\n", phandle, 12713c927cccSTimur Tabi fdt_strerror(ret)); 12723c927cccSTimur Tabi return 0; 12733c927cccSTimur Tabi } 127410aeabd1SKumar Gala } 127510aeabd1SKumar Gala 127610aeabd1SKumar Gala return phandle; 127710aeabd1SKumar Gala } 127810aeabd1SKumar Gala 12792a523f52SShengzhou Liu /* 12802a523f52SShengzhou Liu * fdt_set_node_status: Set status for the given node 12812a523f52SShengzhou Liu * 12822a523f52SShengzhou Liu * @fdt: ptr to device tree 12832a523f52SShengzhou Liu * @nodeoffset: node to update 12842a523f52SShengzhou Liu * @status: FDT_STATUS_OKAY, FDT_STATUS_DISABLED, 12852a523f52SShengzhou Liu * FDT_STATUS_FAIL, FDT_STATUS_FAIL_ERROR_CODE 12862a523f52SShengzhou Liu * @error_code: optional, only used if status is FDT_STATUS_FAIL_ERROR_CODE 12872a523f52SShengzhou Liu */ 12882a523f52SShengzhou Liu int fdt_set_node_status(void *fdt, int nodeoffset, 12892a523f52SShengzhou Liu enum fdt_status status, unsigned int error_code) 12902a523f52SShengzhou Liu { 12912a523f52SShengzhou Liu char buf[16]; 12922a523f52SShengzhou Liu int ret = 0; 12932a523f52SShengzhou Liu 12942a523f52SShengzhou Liu if (nodeoffset < 0) 12952a523f52SShengzhou Liu return nodeoffset; 12962a523f52SShengzhou Liu 12972a523f52SShengzhou Liu switch (status) { 12982a523f52SShengzhou Liu case FDT_STATUS_OKAY: 12992a523f52SShengzhou Liu ret = fdt_setprop_string(fdt, nodeoffset, "status", "okay"); 13002a523f52SShengzhou Liu break; 13012a523f52SShengzhou Liu case FDT_STATUS_DISABLED: 13022a523f52SShengzhou Liu ret = fdt_setprop_string(fdt, nodeoffset, "status", "disabled"); 13032a523f52SShengzhou Liu break; 13042a523f52SShengzhou Liu case FDT_STATUS_FAIL: 13052a523f52SShengzhou Liu ret = fdt_setprop_string(fdt, nodeoffset, "status", "fail"); 13062a523f52SShengzhou Liu break; 13072a523f52SShengzhou Liu case FDT_STATUS_FAIL_ERROR_CODE: 13082a523f52SShengzhou Liu sprintf(buf, "fail-%d", error_code); 13092a523f52SShengzhou Liu ret = fdt_setprop_string(fdt, nodeoffset, "status", buf); 13102a523f52SShengzhou Liu break; 13112a523f52SShengzhou Liu default: 13122a523f52SShengzhou Liu printf("Invalid fdt status: %x\n", status); 13132a523f52SShengzhou Liu ret = -1; 13142a523f52SShengzhou Liu break; 13152a523f52SShengzhou Liu } 13162a523f52SShengzhou Liu 13172a523f52SShengzhou Liu return ret; 13182a523f52SShengzhou Liu } 13192a523f52SShengzhou Liu 13202a523f52SShengzhou Liu /* 13212a523f52SShengzhou Liu * fdt_set_status_by_alias: Set status for the given node given an alias 13222a523f52SShengzhou Liu * 13232a523f52SShengzhou Liu * @fdt: ptr to device tree 13242a523f52SShengzhou Liu * @alias: alias of node to update 13252a523f52SShengzhou Liu * @status: FDT_STATUS_OKAY, FDT_STATUS_DISABLED, 13262a523f52SShengzhou Liu * FDT_STATUS_FAIL, FDT_STATUS_FAIL_ERROR_CODE 13272a523f52SShengzhou Liu * @error_code: optional, only used if status is FDT_STATUS_FAIL_ERROR_CODE 13282a523f52SShengzhou Liu */ 13292a523f52SShengzhou Liu int fdt_set_status_by_alias(void *fdt, const char* alias, 13302a523f52SShengzhou Liu enum fdt_status status, unsigned int error_code) 13312a523f52SShengzhou Liu { 13322a523f52SShengzhou Liu int offset = fdt_path_offset(fdt, alias); 13332a523f52SShengzhou Liu 13342a523f52SShengzhou Liu return fdt_set_node_status(fdt, offset, status, error_code); 13352a523f52SShengzhou Liu } 13362a523f52SShengzhou Liu 1337096eb3f5STom Wai-Hong Tam #if defined(CONFIG_VIDEO) || defined(CONFIG_LCD) 1338beca5a5fSAnatolij Gustschin int fdt_add_edid(void *blob, const char *compat, unsigned char *edid_buf) 1339beca5a5fSAnatolij Gustschin { 1340beca5a5fSAnatolij Gustschin int noff; 1341beca5a5fSAnatolij Gustschin int ret; 1342beca5a5fSAnatolij Gustschin 1343beca5a5fSAnatolij Gustschin noff = fdt_node_offset_by_compatible(blob, -1, compat); 1344beca5a5fSAnatolij Gustschin if (noff != -FDT_ERR_NOTFOUND) { 1345beca5a5fSAnatolij Gustschin debug("%s: %s\n", fdt_get_name(blob, noff, 0), compat); 1346beca5a5fSAnatolij Gustschin add_edid: 1347beca5a5fSAnatolij Gustschin ret = fdt_setprop(blob, noff, "edid", edid_buf, 128); 1348beca5a5fSAnatolij Gustschin if (ret == -FDT_ERR_NOSPACE) { 1349beca5a5fSAnatolij Gustschin ret = fdt_increase_size(blob, 512); 1350beca5a5fSAnatolij Gustschin if (!ret) 1351beca5a5fSAnatolij Gustschin goto add_edid; 1352beca5a5fSAnatolij Gustschin else 1353beca5a5fSAnatolij Gustschin goto err_size; 1354beca5a5fSAnatolij Gustschin } else if (ret < 0) { 1355beca5a5fSAnatolij Gustschin printf("Can't add property: %s\n", fdt_strerror(ret)); 1356beca5a5fSAnatolij Gustschin return ret; 1357beca5a5fSAnatolij Gustschin } 1358beca5a5fSAnatolij Gustschin } 1359beca5a5fSAnatolij Gustschin return 0; 1360beca5a5fSAnatolij Gustschin err_size: 1361beca5a5fSAnatolij Gustschin printf("Can't increase blob size: %s\n", fdt_strerror(ret)); 1362beca5a5fSAnatolij Gustschin return ret; 1363beca5a5fSAnatolij Gustschin } 1364beca5a5fSAnatolij Gustschin #endif 1365bb682001STimur Tabi 1366bb682001STimur Tabi /* 1367bb682001STimur Tabi * Verify the physical address of device tree node for a given alias 1368bb682001STimur Tabi * 1369bb682001STimur Tabi * This function locates the device tree node of a given alias, and then 1370bb682001STimur Tabi * verifies that the physical address of that device matches the given 1371bb682001STimur Tabi * parameter. It displays a message if there is a mismatch. 1372bb682001STimur Tabi * 1373bb682001STimur Tabi * Returns 1 on success, 0 on failure 1374bb682001STimur Tabi */ 1375bb682001STimur Tabi int fdt_verify_alias_address(void *fdt, int anode, const char *alias, u64 addr) 1376bb682001STimur Tabi { 1377bb682001STimur Tabi const char *path; 13788aa5ec6eSKim Phillips const fdt32_t *reg; 1379bb682001STimur Tabi int node, len; 1380bb682001STimur Tabi u64 dt_addr; 1381bb682001STimur Tabi 1382bb682001STimur Tabi path = fdt_getprop(fdt, anode, alias, NULL); 1383bb682001STimur Tabi if (!path) { 1384bb682001STimur Tabi /* If there's no such alias, then it's not a failure */ 1385bb682001STimur Tabi return 1; 1386bb682001STimur Tabi } 1387bb682001STimur Tabi 1388bb682001STimur Tabi node = fdt_path_offset(fdt, path); 1389bb682001STimur Tabi if (node < 0) { 1390bb682001STimur Tabi printf("Warning: device tree alias '%s' points to invalid " 1391bb682001STimur Tabi "node %s.\n", alias, path); 1392bb682001STimur Tabi return 0; 1393bb682001STimur Tabi } 1394bb682001STimur Tabi 1395bb682001STimur Tabi reg = fdt_getprop(fdt, node, "reg", &len); 1396bb682001STimur Tabi if (!reg) { 1397bb682001STimur Tabi printf("Warning: device tree node '%s' has no address.\n", 1398bb682001STimur Tabi path); 1399bb682001STimur Tabi return 0; 1400bb682001STimur Tabi } 1401bb682001STimur Tabi 1402bb682001STimur Tabi dt_addr = fdt_translate_address(fdt, node, reg); 1403bb682001STimur Tabi if (addr != dt_addr) { 1404bb682001STimur Tabi printf("Warning: U-Boot configured device %s at address %llx,\n" 1405bb682001STimur Tabi " but the device tree has it address %llx.\n", 1406bb682001STimur Tabi alias, addr, dt_addr); 1407bb682001STimur Tabi return 0; 1408bb682001STimur Tabi } 1409bb682001STimur Tabi 1410bb682001STimur Tabi return 1; 1411bb682001STimur Tabi } 1412bb682001STimur Tabi 1413bb682001STimur Tabi /* 1414bb682001STimur Tabi * Returns the base address of an SOC or PCI node 1415bb682001STimur Tabi */ 1416bb682001STimur Tabi u64 fdt_get_base_address(void *fdt, int node) 1417bb682001STimur Tabi { 1418bb682001STimur Tabi int size; 1419bb682001STimur Tabi u32 naddr; 14208aa5ec6eSKim Phillips const fdt32_t *prop; 1421bb682001STimur Tabi 1422bb682001STimur Tabi prop = fdt_getprop(fdt, node, "#address-cells", &size); 1423bb682001STimur Tabi if (prop && size == 4) 14248aa5ec6eSKim Phillips naddr = be32_to_cpup(prop); 1425bb682001STimur Tabi else 1426bb682001STimur Tabi naddr = 2; 1427bb682001STimur Tabi 1428bb682001STimur Tabi prop = fdt_getprop(fdt, node, "ranges", &size); 1429bb682001STimur Tabi 1430bb682001STimur Tabi return prop ? fdt_translate_address(fdt, node, prop + naddr) : 0; 1431bb682001STimur Tabi } 1432c48e6868SAlexander Graf 1433c48e6868SAlexander Graf /* 1434c48e6868SAlexander Graf * Read a property of size <prop_len>. Currently only supports 1 or 2 cells. 1435c48e6868SAlexander Graf */ 1436c48e6868SAlexander Graf static int fdt_read_prop(const fdt32_t *prop, int prop_len, int cell_off, 1437c48e6868SAlexander Graf uint64_t *val, int cells) 1438c48e6868SAlexander Graf { 1439c48e6868SAlexander Graf const fdt32_t *prop32 = &prop[cell_off]; 1440c48e6868SAlexander Graf const fdt64_t *prop64 = (const fdt64_t *)&prop[cell_off]; 1441c48e6868SAlexander Graf 1442c48e6868SAlexander Graf if ((cell_off + cells) > prop_len) 1443c48e6868SAlexander Graf return -FDT_ERR_NOSPACE; 1444c48e6868SAlexander Graf 1445c48e6868SAlexander Graf switch (cells) { 1446c48e6868SAlexander Graf case 1: 1447c48e6868SAlexander Graf *val = fdt32_to_cpu(*prop32); 1448c48e6868SAlexander Graf break; 1449c48e6868SAlexander Graf case 2: 1450c48e6868SAlexander Graf *val = fdt64_to_cpu(*prop64); 1451c48e6868SAlexander Graf break; 1452c48e6868SAlexander Graf default: 1453c48e6868SAlexander Graf return -FDT_ERR_NOSPACE; 1454c48e6868SAlexander Graf } 1455c48e6868SAlexander Graf 1456c48e6868SAlexander Graf return 0; 1457c48e6868SAlexander Graf } 1458c48e6868SAlexander Graf 1459c48e6868SAlexander Graf /** 1460c48e6868SAlexander Graf * fdt_read_range - Read a node's n'th range property 1461c48e6868SAlexander Graf * 1462c48e6868SAlexander Graf * @fdt: ptr to device tree 1463c48e6868SAlexander Graf * @node: offset of node 1464c48e6868SAlexander Graf * @n: range index 1465c48e6868SAlexander Graf * @child_addr: pointer to storage for the "child address" field 1466c48e6868SAlexander Graf * @addr: pointer to storage for the CPU view translated physical start 1467c48e6868SAlexander Graf * @len: pointer to storage for the range length 1468c48e6868SAlexander Graf * 1469c48e6868SAlexander Graf * Convenience function that reads and interprets a specific range out of 1470c48e6868SAlexander Graf * a number of the "ranges" property array. 1471c48e6868SAlexander Graf */ 1472c48e6868SAlexander Graf int fdt_read_range(void *fdt, int node, int n, uint64_t *child_addr, 1473c48e6868SAlexander Graf uint64_t *addr, uint64_t *len) 1474c48e6868SAlexander Graf { 1475c48e6868SAlexander Graf int pnode = fdt_parent_offset(fdt, node); 1476c48e6868SAlexander Graf const fdt32_t *ranges; 1477c48e6868SAlexander Graf int pacells; 1478c48e6868SAlexander Graf int acells; 1479c48e6868SAlexander Graf int scells; 1480c48e6868SAlexander Graf int ranges_len; 1481c48e6868SAlexander Graf int cell = 0; 1482c48e6868SAlexander Graf int r = 0; 1483c48e6868SAlexander Graf 1484c48e6868SAlexander Graf /* 1485c48e6868SAlexander Graf * The "ranges" property is an array of 1486c48e6868SAlexander Graf * { <child address> <parent address> <size in child address space> } 1487c48e6868SAlexander Graf * 1488c48e6868SAlexander Graf * All 3 elements can span a diffent number of cells. Fetch their size. 1489c48e6868SAlexander Graf */ 1490c48e6868SAlexander Graf pacells = fdt_getprop_u32_default_node(fdt, pnode, 0, "#address-cells", 1); 1491c48e6868SAlexander Graf acells = fdt_getprop_u32_default_node(fdt, node, 0, "#address-cells", 1); 1492c48e6868SAlexander Graf scells = fdt_getprop_u32_default_node(fdt, node, 0, "#size-cells", 1); 1493c48e6868SAlexander Graf 1494c48e6868SAlexander Graf /* Now try to get the ranges property */ 1495c48e6868SAlexander Graf ranges = fdt_getprop(fdt, node, "ranges", &ranges_len); 1496c48e6868SAlexander Graf if (!ranges) 1497c48e6868SAlexander Graf return -FDT_ERR_NOTFOUND; 1498c48e6868SAlexander Graf ranges_len /= sizeof(uint32_t); 1499c48e6868SAlexander Graf 1500c48e6868SAlexander Graf /* Jump to the n'th entry */ 1501c48e6868SAlexander Graf cell = n * (pacells + acells + scells); 1502c48e6868SAlexander Graf 1503c48e6868SAlexander Graf /* Read <child address> */ 1504c48e6868SAlexander Graf if (child_addr) { 1505c48e6868SAlexander Graf r = fdt_read_prop(ranges, ranges_len, cell, child_addr, 1506c48e6868SAlexander Graf acells); 1507c48e6868SAlexander Graf if (r) 1508c48e6868SAlexander Graf return r; 1509c48e6868SAlexander Graf } 1510c48e6868SAlexander Graf cell += acells; 1511c48e6868SAlexander Graf 1512c48e6868SAlexander Graf /* Read <parent address> */ 1513c48e6868SAlexander Graf if (addr) 1514c48e6868SAlexander Graf *addr = fdt_translate_address(fdt, node, ranges + cell); 1515c48e6868SAlexander Graf cell += pacells; 1516c48e6868SAlexander Graf 1517c48e6868SAlexander Graf /* Read <size in child address space> */ 1518c48e6868SAlexander Graf if (len) { 1519c48e6868SAlexander Graf r = fdt_read_prop(ranges, ranges_len, cell, len, scells); 1520c48e6868SAlexander Graf if (r) 1521c48e6868SAlexander Graf return r; 1522c48e6868SAlexander Graf } 1523c48e6868SAlexander Graf 1524c48e6868SAlexander Graf return 0; 1525c48e6868SAlexander Graf } 1526