X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=common%2Ffdt_support.c;h=dbceec6f2dcc00e4d8c3e4829fa61f4cdb1f6fea;hb=ac2587907b5473779c80e0eb8478b67997c14439;hp=638eca983e877f1dde3e0f14565735692b3d3c42;hpb=d64b9cdcd475eb7f07b49741ded87e24dae4a5fc;p=platform%2Fkernel%2Fu-boot.git diff --git a/common/fdt_support.c b/common/fdt_support.c index 638eca9..dbceec6 100644 --- a/common/fdt_support.c +++ b/common/fdt_support.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -19,8 +20,7 @@ #include #include #include - -DECLARE_GLOBAL_DATA_PTR; +#include /** * fdt_getprop_u32_default_node - Return a node's property or a default @@ -127,14 +127,7 @@ int fdt_find_or_add_subnode(void *fdt, int parentoffset, const char *name) return offset; } -/* rename to CONFIG_OF_STDOUT_PATH ? */ -#if defined(OF_STDOUT_PATH) -static int fdt_fixup_stdout(void *fdt, int chosenoff) -{ - return fdt_setprop(fdt, chosenoff, "linux,stdout-path", - OF_STDOUT_PATH, strlen(OF_STDOUT_PATH) + 1); -} -#elif defined(CONFIG_OF_STDOUT_VIA_ALIAS) && defined(CONFIG_CONS_INDEX) +#if defined(CONFIG_OF_STDOUT_VIA_ALIAS) && defined(CONFIG_CONS_INDEX) static int fdt_fixup_stdout(void *fdt, int chosenoff) { int err; @@ -276,8 +269,18 @@ int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end) return 0; } +/** + * board_fdt_chosen_bootargs - boards may override this function to use + * alternative kernel command line arguments + */ +__weak char *board_fdt_chosen_bootargs(void) +{ + return env_get("bootargs"); +} + int fdt_chosen(void *fdt) { + struct abuf buf = {}; int nodeoffset; int err; char *str; /* used to set string properties */ @@ -293,7 +296,19 @@ int fdt_chosen(void *fdt) if (nodeoffset < 0) return nodeoffset; - str = env_get("bootargs"); + if (IS_ENABLED(CONFIG_BOARD_RNG_SEED) && !board_rng_seed(&buf)) { + err = fdt_setprop(fdt, nodeoffset, "rng-seed", + abuf_data(&buf), abuf_size(&buf)); + abuf_uninit(&buf); + if (err < 0) { + printf("WARNING: could not set rng-seed %s.\n", + fdt_strerror(err)); + return err; + } + } + + str = board_fdt_chosen_bootargs(); + if (str) { err = fdt_setprop(fdt, nodeoffset, "bootargs", str, strlen(str) + 1); @@ -304,6 +319,15 @@ int fdt_chosen(void *fdt) } } + /* add u-boot version */ + err = fdt_setprop(fdt, nodeoffset, "u-boot,version", PLAIN_VERSION, + strlen(PLAIN_VERSION) + 1); + if (err < 0) { + printf("WARNING: could not set u-boot,version %s.\n", + fdt_strerror(err)); + return err; + } + return fdt_fixup_stdout(fdt, nodeoffset); } @@ -370,12 +394,9 @@ void do_fixup_by_compat(void *fdt, const char *compat, debug(" %.2x", *(u8*)(val+i)); debug("\n"); #endif - off = fdt_node_offset_by_compatible(fdt, -1, compat); - while (off != -FDT_ERR_NOTFOUND) { + fdt_for_each_node_by_compatible(off, fdt, -1, compat) if (create || (fdt_get_property(fdt, off, prop, NULL) != NULL)) fdt_setprop(fdt, off, prop, val, len); - off = fdt_node_offset_by_compatible(fdt, off, compat); - } } void do_fixup_by_compat_u32(void *fdt, const char *compat, @@ -419,6 +440,24 @@ static int fdt_pack_reg(const void *fdt, void *buf, u64 *address, u64 *size, #else #define MEMORY_BANKS_MAX 4 #endif + +/** + * fdt_fixup_memory_banks - Update DT memory node + * @blob: Pointer to DT blob + * @start: Pointer to memory start addresses array + * @size: Pointer to memory sizes array + * @banks: Number of memory banks + * + * Return: 0 on success, negative value on failure + * + * Based on the passed number of banks and arrays, the function is able to + * update existing DT memory nodes to match run time detected/changed memory + * configuration. Implementation is handling one specific case with only one + * memory node where multiple tuples could be added/updated. + * The case where multiple memory nodes with a single tuple (base, size) are + * used, this function is only updating the first memory node without removing + * others. + */ int fdt_fixup_memory_banks(void *blob, u64 start[], u64 size[], int banks) { int err, nodeoffset; @@ -578,7 +617,7 @@ void fdt_fixup_ethernet(void *fdt) for (j = 0; j < 6; j++) { mac_addr[j] = tmp ? - simple_strtoul(tmp, &end, 16) : 0; + hextoul(tmp, &end) : 0; if (tmp) tmp = (*end) ? end + 1 : end; } @@ -593,7 +632,7 @@ void fdt_fixup_ethernet(void *fdt) int fdt_record_loadable(void *blob, u32 index, const char *name, uintptr_t load_addr, u32 size, uintptr_t entry_point, - const char *type, const char *os) + const char *type, const char *os, const char *arch) { int err, node; @@ -621,6 +660,8 @@ int fdt_record_loadable(void *blob, u32 index, const char *name, fdt_setprop_string(blob, node, "type", type); if (os) fdt_setprop_string(blob, node, "os", os); + if (arch) + fdt_setprop_string(blob, node, "arch", arch); return node; } @@ -674,8 +715,31 @@ int fdt_shrink_to_minimum(void *blob, uint extrasize) return actualsize; } +/** + * fdt_delete_disabled_nodes: Delete all nodes with status == "disabled" + * + * @blob: ptr to device tree + */ +int fdt_delete_disabled_nodes(void *blob) +{ + while (1) { + int ret, offset; + + offset = fdt_node_offset_by_prop_value(blob, -1, "status", + "disabled", 9); + if (offset < 0) + break; + + ret = fdt_del_node(blob, offset); + if (ret < 0) + return ret; + } + + return 0; +} + #ifdef CONFIG_PCI -#define CONFIG_SYS_PCI_NR_INBOUND_WIN 4 +#define CFG_SYS_PCI_NR_INBOUND_WIN 4 #define FDT_PCI_PREFETCH (0x40000000) #define FDT_PCI_MEM32 (0x02000000) @@ -687,7 +751,7 @@ int fdt_pci_dma_ranges(void *blob, int phb_off, struct pci_controller *hose) { int addrcell, sizecell, len, r; u32 *dma_range; /* sized based on pci addr cells, size-cells, & address-cells */ - u32 dma_ranges[(3 + 2 + 2) * CONFIG_SYS_PCI_NR_INBOUND_WIN]; + u32 dma_ranges[(3 + 2 + 2) * CFG_SYS_PCI_NR_INBOUND_WIN]; addrcell = fdt_getprop_u32_default(blob, "/", "#address-cells", 1); sizecell = fdt_getprop_u32_default(blob, "/", "#size-cells", 1); @@ -947,15 +1011,14 @@ void fdt_fixup_mtdparts(void *blob, const struct node_info *node_info, { struct mtd_device *dev; int i, idx; - int noff; + int noff, parts; bool inited = false; for (i = 0; i < node_info_size; i++) { idx = 0; - noff = -1; - while ((noff = fdt_node_offset_by_compatible(blob, noff, - node_info[i].compat)) >= 0) { + fdt_for_each_node_by_compatible(noff, blob, -1, + node_info[i].compat) { const char *prop; prop = fdt_getprop(blob, noff, "status", NULL); @@ -974,7 +1037,12 @@ void fdt_fixup_mtdparts(void *blob, const struct node_info *node_info, dev = device_find(node_info[i].type, idx++); if (dev) { - if (fdt_node_set_part_info(blob, noff, dev)) + parts = fdt_subnode_offset(blob, noff, + "partitions"); + if (parts < 0) + parts = noff; + + if (fdt_node_set_part_info(blob, parts, dev)) return; /* return on error */ } } @@ -998,8 +1066,8 @@ void fdt_del_node_and_alias(void *blob, const char *alias) /* Max address size we deal with */ #define OF_MAX_ADDR_CELLS 4 #define OF_BAD_ADDR FDT_ADDR_T_NONE -#define OF_CHECK_COUNTS(na, ns) (((na) > 0 && (na) <= OF_MAX_ADDR_CELLS) && \ - ((ns) > 0 || gd_size_cells_0())) +#define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \ + (ns) > 0) /* Debug utility */ #ifdef DEBUG @@ -1344,6 +1412,79 @@ u64 fdt_translate_dma_address(const void *blob, int node_offset, return __of_translate_address(blob, node_offset, in_addr, "dma-ranges"); } +int fdt_get_dma_range(const void *blob, int node, phys_addr_t *cpu, + dma_addr_t *bus, u64 *size) +{ + bool found_dma_ranges = false; + struct of_bus *bus_node; + const fdt32_t *ranges; + int na, ns, pna, pns; + int parent = node; + int ret = 0; + int len; + + /* Find the closest dma-ranges property */ + while (parent >= 0) { + ranges = fdt_getprop(blob, parent, "dma-ranges", &len); + + /* Ignore empty ranges, they imply no translation required */ + if (ranges && len > 0) + break; + + /* Once we find 'dma-ranges', then a missing one is an error */ + if (found_dma_ranges && !ranges) { + ret = -EINVAL; + goto out; + } + + if (ranges) + found_dma_ranges = true; + + parent = fdt_parent_offset(blob, parent); + } + + if (!ranges || parent < 0) { + debug("no dma-ranges found for node %s\n", + fdt_get_name(blob, node, NULL)); + ret = -ENOENT; + goto out; + } + + /* switch to that node */ + node = parent; + parent = fdt_parent_offset(blob, node); + if (parent < 0) { + printf("Found dma-ranges in root node, shouldn't happen\n"); + ret = -EINVAL; + goto out; + } + + /* Get the address sizes both for the bus and its parent */ + bus_node = of_match_bus(blob, node); + bus_node->count_cells(blob, node, &na, &ns); + if (!OF_CHECK_COUNTS(na, ns)) { + printf("%s: Bad cell count for %s\n", __FUNCTION__, + fdt_get_name(blob, node, NULL)); + return -EINVAL; + goto out; + } + + bus_node = of_match_bus(blob, parent); + bus_node->count_cells(blob, parent, &pna, &pns); + if (!OF_CHECK_COUNTS(pna, pns)) { + printf("%s: Bad cell count for %s\n", __FUNCTION__, + fdt_get_name(blob, parent, NULL)); + return -EINVAL; + goto out; + } + + *bus = fdt_read_number(ranges, na); + *cpu = fdt_translate_dma_address(blob, node, ranges + na); + *size = fdt_read_number(ranges + na + pna, ns); +out: + return ret; +} + /** * fdt_node_offset_by_compat_reg: Find a node that matches compatiable and * who's reg property matches a physical cpu address @@ -1356,35 +1497,46 @@ u64 fdt_translate_dma_address(const void *blob, int node_offset, int fdt_node_offset_by_compat_reg(void *blob, const char *compat, phys_addr_t compat_off) { - int len, off = fdt_node_offset_by_compatible(blob, -1, compat); - while (off != -FDT_ERR_NOTFOUND) { + int len, off; + + fdt_for_each_node_by_compatible(off, blob, -1, compat) { const fdt32_t *reg = fdt_getprop(blob, off, "reg", &len); - if (reg) { - if (compat_off == fdt_translate_address(blob, off, reg)) - return off; - } - off = fdt_node_offset_by_compatible(blob, off, compat); + if (reg && compat_off == fdt_translate_address(blob, off, reg)) + return off; } return -FDT_ERR_NOTFOUND; } +static int vnode_offset_by_pathf(void *blob, const char *fmt, va_list ap) +{ + char path[512]; + int len; + + len = vsnprintf(path, sizeof(path), fmt, ap); + if (len < 0 || len + 1 > sizeof(path)) + return -FDT_ERR_NOSPACE; + + return fdt_path_offset(blob, path); +} + /** - * fdt_alloc_phandle: Return next free phandle value + * fdt_node_offset_by_pathf: Find node offset by sprintf formatted path * * @blob: ptr to device tree + * @fmt: path format + * @ap: vsnprintf arguments */ -int fdt_alloc_phandle(void *blob) +int fdt_node_offset_by_pathf(void *blob, const char *fmt, ...) { - int offset; - uint32_t phandle = 0; + va_list ap; + int res; - for (offset = fdt_next_node(blob, -1, NULL); offset >= 0; - offset = fdt_next_node(blob, offset, NULL)) { - phandle = max(phandle, fdt_get_phandle(blob, offset)); - } + va_start(ap, fmt); + res = vnode_offset_by_pathf(blob, fmt, ap); + va_end(ap); - return phandle + 1; + return res; } /* @@ -1415,20 +1567,12 @@ int fdt_set_phandle(void *fdt, int nodeoffset, uint32_t phandle) #endif ret = fdt_setprop_cell(fdt, nodeoffset, "phandle", phandle); - if (ret < 0) - return ret; - - /* - * For now, also set the deprecated "linux,phandle" property, so that we - * don't break older kernels. - */ - ret = fdt_setprop_cell(fdt, nodeoffset, "linux,phandle", phandle); return ret; } /* - * fdt_create_phandle: Create a phandle property for the given node + * fdt_create_phandle: Get or create a phandle property for the given node * * @fdt: ptr to device tree * @nodeoffset: node to update @@ -1436,13 +1580,19 @@ int fdt_set_phandle(void *fdt, int nodeoffset, uint32_t phandle) unsigned int fdt_create_phandle(void *fdt, int nodeoffset) { /* see if there is a phandle already */ - int phandle = fdt_get_phandle(fdt, nodeoffset); + uint32_t phandle = fdt_get_phandle(fdt, nodeoffset); /* if we got 0, means no phandle so create one */ if (phandle == 0) { int ret; - phandle = fdt_alloc_phandle(fdt); + ret = fdt_generate_phandle(fdt, &phandle); + if (ret < 0) { + printf("Can't generate phandle: %s\n", + fdt_strerror(ret)); + return 0; + } + ret = fdt_set_phandle(fdt, nodeoffset, phandle); if (ret < 0) { printf("Can't set phandle %u: %s\n", phandle, @@ -1454,19 +1604,60 @@ unsigned int fdt_create_phandle(void *fdt, int nodeoffset) return phandle; } +/** + * fdt_create_phandle_by_compatible: Get or create a phandle for first node with + * given compatible + * + * @fdt: ptr to device tree + * @compat: node's compatible string + */ +unsigned int fdt_create_phandle_by_compatible(void *fdt, const char *compat) +{ + int offset = fdt_node_offset_by_compatible(fdt, -1, compat); + + if (offset < 0) { + printf("Can't find node with compatible \"%s\": %s\n", compat, + fdt_strerror(offset)); + return 0; + } + + return fdt_create_phandle(fdt, offset); +} + +/** + * fdt_create_phandle_by_pathf: Get or create a phandle for node given by + * sprintf-formatted path + * + * @fdt: ptr to device tree + * @fmt, ...: path format string and arguments to pass to sprintf + */ +unsigned int fdt_create_phandle_by_pathf(void *fdt, const char *fmt, ...) +{ + va_list ap; + int offset; + + va_start(ap, fmt); + offset = vnode_offset_by_pathf(fdt, fmt, ap); + va_end(ap); + + if (offset < 0) { + printf("Can't find node by given path: %s\n", + fdt_strerror(offset)); + return 0; + } + + return fdt_create_phandle(fdt, offset); +} + /* * fdt_set_node_status: Set status for the given node * * @fdt: ptr to device tree * @nodeoffset: node to update - * @status: FDT_STATUS_OKAY, FDT_STATUS_DISABLED, - * FDT_STATUS_FAIL, FDT_STATUS_FAIL_ERROR_CODE - * @error_code: optional, only used if status is FDT_STATUS_FAIL_ERROR_CODE + * @status: FDT_STATUS_OKAY, FDT_STATUS_DISABLED, FDT_STATUS_FAIL */ -int fdt_set_node_status(void *fdt, int nodeoffset, - enum fdt_status status, unsigned int error_code) +int fdt_set_node_status(void *fdt, int nodeoffset, enum fdt_status status) { - char buf[16]; int ret = 0; if (nodeoffset < 0) @@ -1482,10 +1673,6 @@ int fdt_set_node_status(void *fdt, int nodeoffset, case FDT_STATUS_FAIL: ret = fdt_setprop_string(fdt, nodeoffset, "status", "fail"); break; - case FDT_STATUS_FAIL_ERROR_CODE: - sprintf(buf, "fail-%d", error_code); - ret = fdt_setprop_string(fdt, nodeoffset, "status", buf); - break; default: printf("Invalid fdt status: %x\n", status); ret = -1; @@ -1500,46 +1687,58 @@ int fdt_set_node_status(void *fdt, int nodeoffset, * * @fdt: ptr to device tree * @alias: alias of node to update - * @status: FDT_STATUS_OKAY, FDT_STATUS_DISABLED, - * FDT_STATUS_FAIL, FDT_STATUS_FAIL_ERROR_CODE - * @error_code: optional, only used if status is FDT_STATUS_FAIL_ERROR_CODE + * @status: FDT_STATUS_OKAY, FDT_STATUS_DISABLED, FDT_STATUS_FAIL */ int fdt_set_status_by_alias(void *fdt, const char* alias, - enum fdt_status status, unsigned int error_code) + enum fdt_status status) { int offset = fdt_path_offset(fdt, alias); - return fdt_set_node_status(fdt, offset, status, error_code); + return fdt_set_node_status(fdt, offset, status); } -#if defined(CONFIG_VIDEO) || defined(CONFIG_LCD) -int fdt_add_edid(void *blob, const char *compat, unsigned char *edid_buf) +/** + * fdt_set_status_by_compatible: Set node status for first node with given + * compatible + * + * @fdt: ptr to device tree + * @compat: node's compatible string + * @status: FDT_STATUS_OKAY, FDT_STATUS_DISABLED, FDT_STATUS_FAIL + */ +int fdt_set_status_by_compatible(void *fdt, const char *compat, + enum fdt_status status) { - int noff; - int ret; + int offset = fdt_node_offset_by_compatible(fdt, -1, compat); - noff = fdt_node_offset_by_compatible(blob, -1, compat); - if (noff != -FDT_ERR_NOTFOUND) { - debug("%s: %s\n", fdt_get_name(blob, noff, 0), compat); -add_edid: - ret = fdt_setprop(blob, noff, "edid", edid_buf, 128); - if (ret == -FDT_ERR_NOSPACE) { - ret = fdt_increase_size(blob, 512); - if (!ret) - goto add_edid; - else - goto err_size; - } else if (ret < 0) { - printf("Can't add property: %s\n", fdt_strerror(ret)); - return ret; - } - } - return 0; -err_size: - printf("Can't increase blob size: %s\n", fdt_strerror(ret)); - return ret; + if (offset < 0) + return offset; + + return fdt_set_node_status(fdt, offset, status); +} + +/** + * fdt_set_status_by_pathf: Set node status for node given by sprintf-formatted + * path + * + * @fdt: ptr to device tree + * @status: FDT_STATUS_OKAY, FDT_STATUS_DISABLED, FDT_STATUS_FAIL + * @fmt, ...: path format string and arguments to pass to sprintf + */ +int fdt_set_status_by_pathf(void *fdt, enum fdt_status status, const char *fmt, + ...) +{ + va_list ap; + int offset; + + va_start(ap, fmt); + offset = vnode_offset_by_pathf(fdt, fmt, ap); + va_end(ap); + + if (offset < 0) + return offset; + + return fdt_set_node_status(fdt, offset, status); } -#endif /* * Verify the physical address of device tree node for a given alias @@ -1602,22 +1801,36 @@ u64 fdt_get_base_address(const void *fdt, int node) } /* - * Read a property of size . Currently only supports 1 or 2 cells. + * Read a property of size . Currently only supports 1 or 2 cells, + * or 3 cells specially for a PCI address. */ static int fdt_read_prop(const fdt32_t *prop, int prop_len, int cell_off, uint64_t *val, int cells) { - const fdt32_t *prop32 = &prop[cell_off]; - const unaligned_fdt64_t *prop64 = (const fdt64_t *)&prop[cell_off]; + const fdt32_t *prop32; + const unaligned_fdt64_t *prop64; if ((cell_off + cells) > prop_len) return -FDT_ERR_NOSPACE; + prop32 = &prop[cell_off]; + + /* + * Special handling for PCI address in PCI bus + * + * PCI child address is made up of 3 cells. Advance the cell offset + * by 1 so that the PCI child address can be correctly read. + */ + if (cells == 3) + cell_off += 1; + prop64 = (const fdt64_t *)&prop[cell_off]; + switch (cells) { case 1: *val = fdt32_to_cpu(*prop32); break; case 2: + case 3: *val = fdt64_to_cpu(*prop64); break; default: @@ -1816,3 +2029,49 @@ int fdt_overlay_apply_verbose(void *fdt, void *fdto) return err; } #endif + +/** + * fdt_valid() - Check if an FDT is valid. If not, change it to NULL + * + * @blobp: Pointer to FDT pointer + * Return: 1 if OK, 0 if bad (in which case *blobp is set to NULL) + */ +int fdt_valid(struct fdt_header **blobp) +{ + const void *blob = *blobp; + int err; + + if (!blob) { + printf("The address of the fdt is invalid (NULL).\n"); + return 0; + } + + err = fdt_check_header(blob); + if (err == 0) + return 1; /* valid */ + + if (err < 0) { + printf("libfdt fdt_check_header(): %s", fdt_strerror(err)); + /* + * Be more informative on bad version. + */ + if (err == -FDT_ERR_BADVERSION) { + if (fdt_version(blob) < + FDT_FIRST_SUPPORTED_VERSION) { + printf(" - too old, fdt %d < %d", + fdt_version(blob), + FDT_FIRST_SUPPORTED_VERSION); + } + if (fdt_last_comp_version(blob) > + FDT_LAST_SUPPORTED_VERSION) { + printf(" - too new, fdt %d > %d", + fdt_version(blob), + FDT_LAST_SUPPORTED_VERSION); + } + } + printf("\n"); + *blobp = NULL; + return 0; + } + return 1; +}