diff options
author | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
---|---|---|
committer | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/core/device.c | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/core/device.c')
-rw-r--r-- | roms/skiboot/core/device.c | 1128 |
1 files changed, 1128 insertions, 0 deletions
diff --git a/roms/skiboot/core/device.c b/roms/skiboot/core/device.c new file mode 100644 index 000000000..b102dd973 --- /dev/null +++ b/roms/skiboot/core/device.c @@ -0,0 +1,1128 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +/* + * Manipulate the device tree + * + * Copyright 2013-2019 IBM Corp. + */ + +#include <stdarg.h> +#include <device.h> +#include <stdlib.h> +#include <skiboot.h> +#include <libfdt/libfdt.h> +#include <libfdt/libfdt_internal.h> +#include <ccan/str/str.h> +#include <ccan/endian/endian.h> +#include <inttypes.h> + +/* Used to give unique handles. */ +u32 last_phandle = 0; + +struct dt_node *dt_root; +struct dt_node *dt_chosen; + +static const char *take_name(const char *name) +{ + if (!is_rodata(name) && !(name = strdup(name))) { + prerror("Failed to allocate copy of name"); + abort(); + } + return name; +} + +static void free_name(const char *name) +{ + if (!is_rodata(name)) + free((char *)name); +} + +static struct dt_node *new_node(const char *name) +{ + struct dt_node *node = malloc(sizeof *node); + if (!node) { + prerror("Failed to allocate node\n"); + abort(); + } + + node->name = take_name(name); + node->parent = NULL; + list_head_init(&node->properties); + list_head_init(&node->children); + /* FIXME: locking? */ + node->phandle = new_phandle(); + return node; +} + +struct dt_node *dt_new_root(const char *name) +{ + return new_node(name); +} + +static const char *get_unitname(const struct dt_node *node) +{ + const char *c = strchr(node->name, '@'); + + if (!c) + return NULL; + + return c + 1; +} + +int dt_cmp_subnodes(const struct dt_node *a, const struct dt_node *b) +{ + const char *a_unit = get_unitname(a); + const char *b_unit = get_unitname(b); + + ptrdiff_t basenamelen = a_unit - a->name; + + /* sort hex unit addresses by number */ + if (a_unit && b_unit && !strncmp(a->name, b->name, basenamelen)) { + unsigned long long a_num, b_num; + char *a_end, *b_end; + + a_num = strtoul(a_unit, &a_end, 16); + b_num = strtoul(b_unit, &b_end, 16); + + /* only compare if the unit addr parsed correctly */ + if (*a_end == 0 && *b_end == 0) + return (a_num > b_num) - (a_num < b_num); + } + + return strcmp(a->name, b->name); +} + +bool dt_attach_root(struct dt_node *parent, struct dt_node *root) +{ + struct dt_node *node; + + assert(!root->parent); + + if (list_empty(&parent->children)) { + list_add(&parent->children, &root->list); + root->parent = parent; + + return true; + } + + dt_for_each_child(parent, node) { + int cmp = dt_cmp_subnodes(node, root); + + /* Look for duplicates */ + if (cmp == 0) { + prerror("DT: %s failed, duplicate %s\n", + __func__, root->name); + return false; + } + + /* insert before the first node that's larger + * the the node we're inserting */ + if (cmp > 0) + break; + } + + list_add_before(&parent->children, &root->list, &node->list); + root->parent = parent; + + return true; +} + +static inline void dt_destroy(struct dt_node *dn) +{ + if (!dn) + return; + + free_name(dn->name); + free(dn); +} + +struct dt_node *dt_new(struct dt_node *parent, const char *name) +{ + struct dt_node *new; + assert(parent); + + new = new_node(name); + if (!dt_attach_root(parent, new)) { + dt_destroy(new); + return NULL; + } + return new; +} + +/* + * low level variant, we export this because there are "weird" address + * formats, such as LPC/ISA bus addresses which have a letter to identify + * which bus space the address is inside of. + */ +struct dt_node *__dt_find_by_name_addr(struct dt_node *parent, const char *name, + const char *addr) +{ + struct dt_node *node; + + if (list_empty(&parent->children)) + return NULL; + + dt_for_each_child(parent, node) { + const char *unit = get_unitname(node); + int len; + + if (!unit) + continue; + + /* match the name */ + len = (int) (unit - node->name) - 1; + if (strncmp(node->name, name, len)) + continue; + + /* match the unit */ + if (strcmp(unit, addr) == 0) + return node; + } + + dt_for_each_child(parent, node) { + struct dt_node *ret = __dt_find_by_name_addr(node, name, addr); + + if (ret) + return ret; + } + + return NULL; +} + +struct dt_node *dt_find_by_name_addr(struct dt_node *parent, const char *name, + uint64_t addr) +{ + char addr_str[16 + 1]; /* max size of a 64bit int */ + snprintf(addr_str, sizeof(addr_str), "%" PRIx64, addr); + + return __dt_find_by_name_addr(parent, name, addr_str); +} + +struct dt_node *dt_new_addr(struct dt_node *parent, const char *name, + uint64_t addr) +{ + char *lname; + struct dt_node *new; + size_t len; + + assert(parent); + len = strlen(name) + STR_MAX_CHARS(addr) + 2; + lname = malloc(len); + if (!lname) + return NULL; + snprintf(lname, len, "%s@%llx", name, (long long)addr); + new = new_node(lname); + free(lname); + if (!dt_attach_root(parent, new)) { + dt_destroy(new); + return NULL; + } + return new; +} + +struct dt_node *dt_new_2addr(struct dt_node *parent, const char *name, + uint64_t addr0, uint64_t addr1) +{ + char *lname; + struct dt_node *new; + size_t len; + assert(parent); + + len = strlen(name) + 2*STR_MAX_CHARS(addr0) + 3; + lname = malloc(len); + if (!lname) + return NULL; + snprintf(lname, len, "%s@%llx,%llx", + name, (long long)addr0, (long long)addr1); + new = new_node(lname); + free(lname); + if (!dt_attach_root(parent, new)) { + dt_destroy(new); + return NULL; + } + return new; +} + +static struct dt_node *__dt_copy(struct dt_node *node, struct dt_node *parent, + bool root) +{ + struct dt_property *prop, *new_prop; + struct dt_node *new_node, *child; + + new_node = dt_new(parent, node->name); + if (!new_node) + return NULL; + + list_for_each(&node->properties, prop, list) { + new_prop = dt_add_property(new_node, prop->name, prop->prop, + prop->len); + if (!new_prop) + goto fail; + } + + list_for_each(&node->children, child, list) { + child = __dt_copy(child, new_node, false); + if (!child) + goto fail; + } + + return new_node; + +fail: + /* dt_free will recurse for us, so only free when we unwind to the + * top-level failure */ + if (root) + dt_free(new_node); + return NULL; +} + +struct dt_node *dt_copy(struct dt_node *node, struct dt_node *parent) +{ + return __dt_copy(node, parent, true); +} + +char *dt_get_path(const struct dt_node *node) +{ + unsigned int len = 0; + const struct dt_node *n; + char *path, *p; + + /* Dealing with NULL is for test/debug purposes */ + if (!node) + return strdup("<NULL>"); + + for (n = node; n; n = n->parent) { + len += strlen(n->name); + if (n->parent || n == node) + len++; + } + path = zalloc(len + 1); + assert(path); + p = path + len; + for (n = node; n; n = n->parent) { + len = strlen(n->name); + p -= len; + memcpy(p, n->name, len); + if (n->parent || n == node) + *(--p) = '/'; + } + assert(p == path); + + return p; +} + +static const char *__dt_path_split(const char *p, + const char **namep, unsigned int *namel, + const char **addrp, unsigned int *addrl) +{ + const char *at, *sl; + + *namel = *addrl = 0; + + /* Skip initial '/' */ + while (*p == '/') + p++; + + /* Check empty path */ + if (*p == 0) + return p; + + at = strchr(p, '@'); + sl = strchr(p, '/'); + if (sl == NULL) + sl = p + strlen(p); + if (sl < at) + at = NULL; + if (at) { + *addrp = at + 1; + *addrl = sl - at - 1; + } + *namep = p; + *namel = at ? (at - p) : (sl - p); + + return sl; +} + +struct dt_node *dt_find_by_path(struct dt_node *root, const char *path) +{ + struct dt_node *n; + const char *pn, *pa, *p = path, *nn, *na; + unsigned int pnl, pal, nnl, nal; + bool match; + + /* Walk path components */ + while (*p) { + /* Extract next path component */ + p = __dt_path_split(p, &pn, &pnl, &pa, &pal); + if (pnl == 0 && pal == 0) + break; + + /* Compare with each child node */ + match = false; + list_for_each(&root->children, n, list) { + match = true; + __dt_path_split(n->name, &nn, &nnl, &na, &nal); + if (pnl && (pnl != nnl || strncmp(pn, nn, pnl))) + match = false; + if (pal && (pal != nal || strncmp(pa, na, pal))) + match = false; + if (match) { + root = n; + break; + } + } + + /* No child match */ + if (!match) + return NULL; + } + return root; +} + +struct dt_node *dt_find_by_name(struct dt_node *root, const char *name) +{ + struct dt_node *child, *match; + + list_for_each(&root->children, child, list) { + if (!strcmp(child->name, name)) + return child; + + match = dt_find_by_name(child, name); + if (match) + return match; + } + + return NULL; +} + + +struct dt_node *dt_new_check(struct dt_node *parent, const char *name) +{ + struct dt_node *node = dt_find_by_name(parent, name); + + if (!node) { + node = dt_new(parent, name); + assert(node); + } + + return node; +} + + +struct dt_node *dt_find_by_phandle(struct dt_node *root, u32 phandle) +{ + struct dt_node *node; + + dt_for_each_node(root, node) + if (node->phandle == phandle) + return node; + return NULL; +} + +static struct dt_property *new_property(struct dt_node *node, + const char *name, size_t size) +{ + struct dt_property *p = malloc(sizeof(*p) + size); + char *path; + + if (!p) { + path = dt_get_path(node); + prerror("Failed to allocate property \"%s\" for %s of %zu bytes\n", + name, path, size); + free(path); + abort(); + } + if (dt_find_property(node, name)) { + path = dt_get_path(node); + prerror("Duplicate property \"%s\" in node %s\n", + name, path); + free(path); + abort(); + + } + + p->name = take_name(name); + p->len = size; + list_add_tail(&node->properties, &p->list); + return p; +} + +struct dt_property *dt_add_property(struct dt_node *node, + const char *name, + const void *val, size_t size) +{ + struct dt_property *p; + + /* + * Filter out phandle properties, we re-generate them + * when flattening + */ + if (strcmp(name, "linux,phandle") == 0 || + strcmp(name, "phandle") == 0) { + assert(size == 4); + node->phandle = *(const u32 *)val; + if (node->phandle >= last_phandle) + set_last_phandle(node->phandle); + return NULL; + } + + p = new_property(node, name, size); + if (size) + memcpy(p->prop, val, size); + return p; +} + +void dt_resize_property(struct dt_property **prop, size_t len) +{ + size_t new_len = sizeof(**prop) + len; + + *prop = realloc(*prop, new_len); + (*prop)->len = len; + + /* Fix up linked lists in case we moved. (note: not an empty list). */ + (*prop)->list.next->prev = &(*prop)->list; + (*prop)->list.prev->next = &(*prop)->list; +} + +struct dt_property *dt_add_property_string(struct dt_node *node, + const char *name, + const char *value) +{ + size_t len = 0; + if (value) + len = strlen(value) + 1; + return dt_add_property(node, name, value, len); +} + +struct dt_property *dt_add_property_nstr(struct dt_node *node, + const char *name, + const char *value, unsigned int vlen) +{ + struct dt_property *p; + char *tmp = zalloc(vlen + 1); + + if (!tmp) + return NULL; + + strncpy(tmp, value, vlen); + p = dt_add_property(node, name, tmp, strlen(tmp)+1); + free(tmp); + + return p; +} + +struct dt_property *__dt_add_property_cells(struct dt_node *node, + const char *name, + int count, ...) +{ + struct dt_property *p; + fdt32_t *val; + unsigned int i; + va_list args; + + p = new_property(node, name, count * sizeof(u32)); + val = (fdt32_t *)p->prop; + va_start(args, count); + for (i = 0; i < count; i++) + val[i] = cpu_to_fdt32(va_arg(args, u32)); + va_end(args); + return p; +} + +struct dt_property *__dt_add_property_u64s(struct dt_node *node, + const char *name, + int count, ...) +{ + struct dt_property *p; + fdt64_t *val; + unsigned int i; + va_list args; + + p = new_property(node, name, count * sizeof(u64)); + val = (fdt64_t *)p->prop; + va_start(args, count); + for (i = 0; i < count; i++) + val[i] = cpu_to_fdt64(va_arg(args, u64)); + va_end(args); + return p; +} + +struct dt_property *__dt_add_property_strings(struct dt_node *node, + const char *name, + int count, ...) +{ + struct dt_property *p; + unsigned int i, size; + va_list args; + const char *sstr; + char *s; + + va_start(args, count); + for (i = size = 0; i < count; i++) { + sstr = va_arg(args, const char *); + if (sstr) + size += strlen(sstr) + 1; + } + va_end(args); + if (!size) + size = 1; + p = new_property(node, name, size); + s = (char *)p->prop; + *s = 0; + va_start(args, count); + for (i = 0; i < count; i++) { + sstr = va_arg(args, const char *); + if (sstr) { + strcpy(s, sstr); + s = s + strlen(sstr) + 1; + } + } + va_end(args); + return p; +} + +void dt_del_property(struct dt_node *node, struct dt_property *prop) +{ + list_del_from(&node->properties, &prop->list); + free_name(prop->name); + free(prop); +} + +u32 dt_property_get_cell(const struct dt_property *prop, u32 index) +{ + assert(prop->len >= (index+1)*sizeof(u32)); + /* Always aligned, so this works. */ + return fdt32_to_cpu(((const fdt32_t *)prop->prop)[index]); +} + +u64 dt_property_get_u64(const struct dt_property *prop, u32 index) +{ + assert(prop->len >= (index+1)*sizeof(u64)); + /* Always aligned, so this works. */ + return fdt64_to_cpu(((const fdt64_t *)prop->prop)[index]); +} + +void dt_property_set_cell(struct dt_property *prop, u32 index, u32 val) +{ + assert(prop->len >= (index+1)*sizeof(u32)); + /* Always aligned, so this works. */ + ((fdt32_t *)prop->prop)[index] = cpu_to_fdt32(val); +} + +/* First child of this node. */ +struct dt_node *dt_first(const struct dt_node *root) +{ + return list_top(&root->children, struct dt_node, list); +} + +/* Return next node, or NULL. */ +struct dt_node *dt_next(const struct dt_node *root, + const struct dt_node *prev) +{ + if (!prev) { + struct dt_node *first = dt_first(root); + + if (!first) + return NULL; + else + return first; + } + + /* Children? */ + if (!list_empty(&prev->children)) + return dt_first(prev); + + do { + /* More siblings? */ + if (prev->list.next != &prev->parent->children.n) + return list_entry(prev->list.next, struct dt_node,list); + + /* No more siblings, move up to parent. */ + prev = prev->parent; + } while (prev != root); + + return NULL; +} + +struct dt_property *__dt_find_property(struct dt_node *node, const char *name) +{ + struct dt_property *i; + + list_for_each(&node->properties, i, list) + if (strcmp(i->name, name) == 0) + return i; + return NULL; +} + +const struct dt_property *dt_find_property(const struct dt_node *node, + const char *name) +{ + const struct dt_property *i; + + list_for_each(&node->properties, i, list) + if (strcmp(i->name, name) == 0) + return i; + return NULL; +} + +void dt_check_del_prop(struct dt_node *node, const char *name) +{ + struct dt_property *p; + + p = __dt_find_property(node, name); + if (p) + dt_del_property(node, p); +} +const struct dt_property *dt_require_property(const struct dt_node *node, + const char *name, int wanted_len) +{ + const struct dt_property *p = dt_find_property(node, name); + + if (!p) { + const char *path = dt_get_path(node); + + prerror("DT: Missing required property %s/%s\n", + path, name); + assert(false); + } + if (wanted_len >= 0 && p->len != wanted_len) { + const char *path = dt_get_path(node); + + prerror("DT: Unexpected property length %s/%s\n", + path, name); + prerror("DT: Expected len: %d got len: %zu\n", + wanted_len, p->len); + assert(false); + } + + return p; +} + +bool dt_has_node_property(const struct dt_node *node, + const char *name, const char *val) +{ + const struct dt_property *p = dt_find_property(node, name); + + if (!p) + return false; + if (!val) + return true; + + return p->len == strlen(val) + 1 && memcmp(p->prop, val, p->len) == 0; +} + +bool dt_prop_find_string(const struct dt_property *p, const char *s) +{ + const char *c, *end; + + if (!p) + return false; + c = p->prop; + end = c + p->len; + + while(c < end) { + if (!strcasecmp(s, c)) + return true; + c += strlen(c) + 1; + } + return false; +} + +bool dt_node_is_compatible(const struct dt_node *node, const char *compat) +{ + const struct dt_property *p = dt_find_property(node, "compatible"); + + return dt_prop_find_string(p, compat); +} + +struct dt_node *dt_find_compatible_node(struct dt_node *root, + struct dt_node *prev, + const char *compat) +{ + struct dt_node *node = prev; + + while ((node = dt_next(root, node))) + if (dt_node_is_compatible(node, compat)) + return node; + return NULL; +} + +u64 dt_prop_get_u64(const struct dt_node *node, const char *prop) +{ + const struct dt_property *p = dt_require_property(node, prop, 8); + + return ((u64)dt_property_get_cell(p, 0) << 32) + | dt_property_get_cell(p, 1); +} + +u64 dt_prop_get_u64_def(const struct dt_node *node, const char *prop, u64 def) +{ + const struct dt_property *p = dt_find_property(node, prop); + + if (!p) + return def; + + return ((u64)dt_property_get_cell(p, 0) << 32) + | dt_property_get_cell(p, 1); +} + +u32 dt_prop_get_u32(const struct dt_node *node, const char *prop) +{ + const struct dt_property *p = dt_require_property(node, prop, 4); + + return dt_property_get_cell(p, 0); +} + +u32 dt_prop_get_u32_def(const struct dt_node *node, const char *prop, u32 def) +{ + const struct dt_property *p = dt_find_property(node, prop); + + if (!p) + return def; + + return dt_property_get_cell(p, 0); +} + +const void *dt_prop_get(const struct dt_node *node, const char *prop) +{ + const struct dt_property *p = dt_require_property(node, prop, -1); + + return p->prop; +} + +const void *dt_prop_get_def(const struct dt_node *node, const char *prop, + void *def) +{ + const struct dt_property *p = dt_find_property(node, prop); + + return p ? p->prop : def; +} + +const void *dt_prop_get_def_size(const struct dt_node *node, const char *prop, + void *def, size_t *len) +{ + const struct dt_property *p = dt_find_property(node, prop); + *len = 0; + if (p) + *len = p->len; + + return p ? p->prop : def; +} + +u32 dt_prop_get_cell(const struct dt_node *node, const char *prop, u32 cell) +{ + const struct dt_property *p = dt_require_property(node, prop, -1); + + return dt_property_get_cell(p, cell); +} + +u32 dt_prop_get_cell_def(const struct dt_node *node, const char *prop, + u32 cell, u32 def) +{ + const struct dt_property *p = dt_find_property(node, prop); + + if (!p) + return def; + + return dt_property_get_cell(p, cell); +} + +void dt_free(struct dt_node *node) +{ + struct dt_node *child; + struct dt_property *p; + + while ((child = list_top(&node->children, struct dt_node, list))) + dt_free(child); + + while ((p = list_pop(&node->properties, struct dt_property, list))) { + free_name(p->name); + free(p); + } + + if (node->parent) + list_del_from(&node->parent->children, &node->list); + dt_destroy(node); +} + +int dt_expand_node(struct dt_node *node, const void *fdt, int fdt_node) +{ + const struct fdt_property *prop; + int offset, nextoffset, err; + struct dt_node *child; + const char *name; + uint32_t tag; + + if (((err = fdt_check_header(fdt)) != 0) + || ((err = fdt_check_node_offset_(fdt, fdt_node)) < 0)) { + prerror("FDT: Error %d parsing node 0x%x\n", err, fdt_node); + return -1; + } + + nextoffset = err; + do { + offset = nextoffset; + + tag = fdt_next_tag(fdt, offset, &nextoffset); + switch (tag) { + case FDT_PROP: + prop = fdt_offset_ptr_(fdt, offset); + name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + dt_add_property(node, name, prop->data, + fdt32_to_cpu(prop->len)); + break; + case FDT_BEGIN_NODE: + name = fdt_get_name(fdt, offset, NULL); + child = dt_new_root(name); + assert(child); + nextoffset = dt_expand_node(child, fdt, offset); + + /* + * This may fail in case of duplicate, keep it + * going for now, we may ultimately want to + * assert + */ + if (!dt_attach_root(node, child)) + /** + * @fwts-label DTHasDuplicateNodeID + * @fwts-advice OPAL will parse the Flattened + * Device Tree(FDT), which can be generated + * from different firmware sources. During + * expansion of FDT, OPAL observed a node + * assigned multiple times (a duplicate). This + * indicates either a Hostboot bug *OR*, more + * likely, a bug in the platform XML. Check + * the platform XML for duplicate IDs for + * this type of device. Because of this + * duplicate node, OPAL won't add the hardware + * device found with a duplicate node ID into + * DT, rendering the corresponding device not + * functional. + */ + prlog(PR_ERR, "DT: Found duplicate node: %s\n", + child->name); + break; + case FDT_END: + return -1; + } + } while (tag != FDT_END_NODE); + + return nextoffset; +} + +void dt_expand(const void *fdt) +{ + prlog(PR_DEBUG, "FDT: Parsing fdt @%p\n", fdt); + + if (dt_expand_node(dt_root, fdt, 0) < 0) + abort(); +} + +u64 dt_get_number(const void *pdata, unsigned int cells) +{ + const __be32 *p = pdata; + u64 ret = 0; + + while(cells--) + ret = (ret << 32) | be32_to_cpu(*(p++)); + return ret; +} + +u32 dt_n_address_cells(const struct dt_node *node) +{ + if (!node->parent) + return 0; + return dt_prop_get_u32_def(node->parent, "#address-cells", 2); +} + +u32 dt_n_size_cells(const struct dt_node *node) +{ + if (!node->parent) + return 0; + return dt_prop_get_u32_def(node->parent, "#size-cells", 1); +} + +u64 dt_get_address(const struct dt_node *node, unsigned int index, + u64 *out_size) +{ + const struct dt_property *p; + u32 na = dt_n_address_cells(node); + u32 ns = dt_n_size_cells(node); + u32 pos, n; + + p = dt_require_property(node, "reg", -1); + n = (na + ns) * sizeof(u32); + pos = n * index; + assert((pos + n) <= p->len); + if (out_size) + *out_size = dt_get_number(p->prop + pos + na * sizeof(u32), ns); + return dt_get_number(p->prop + pos, na); +} + +u32 __dt_get_chip_id(const struct dt_node *node) +{ + const struct dt_property *prop; + + for (; node; node = node->parent) { + prop = dt_find_property(node, "ibm,chip-id"); + if (prop) + return dt_property_get_cell(prop, 0); + } + return 0xffffffff; +} + +u32 dt_get_chip_id(const struct dt_node *node) +{ + u32 id = __dt_get_chip_id(node); + assert(id != 0xffffffff); + return id; +} + +struct dt_node *dt_find_compatible_node_on_chip(struct dt_node *root, + struct dt_node *prev, + const char *compat, + uint32_t chip_id) +{ + struct dt_node *node = prev; + + while ((node = dt_next(root, node))) { + u32 cid = __dt_get_chip_id(node); + if (cid == chip_id && + dt_node_is_compatible(node, compat)) + return node; + } + return NULL; +} + +unsigned int dt_count_addresses(const struct dt_node *node) +{ + const struct dt_property *p; + u32 na = dt_n_address_cells(node); + u32 ns = dt_n_size_cells(node); + u32 n; + + p = dt_require_property(node, "reg", -1); + n = (na + ns) * sizeof(u32); + + if (n == 0) + return 0; + + return p->len / n; +} + +/* Translates an address from the given bus into its parent's address space */ +static u64 dt_translate_one(const struct dt_node *bus, u64 addr) +{ + u32 ranges_count, na, ns, parent_na; + const struct dt_property *p; + const u32 *ranges; + int i, stride; + + assert(bus->parent); + + na = dt_prop_get_u32_def(bus, "#address-cells", 2); + ns = dt_prop_get_u32_def(bus, "#size-cells", 2); + parent_na = dt_n_address_cells(bus); + + stride = na + ns + parent_na; + + /* + * FIXME: We should handle arbitrary length addresses, rather than + * limiting it to 64bit. If someone wants/needs that they + * can implement the bignum math for it :) + */ + assert(na <= 2); + assert(parent_na <= 2); + + /* We should never be trying to translate an address without a ranges */ + p = dt_require_property(bus, "ranges", -1); + + ranges = (u32 *) &p->prop; + ranges_count = (p->len / 4) / (na + parent_na + ns); + + /* An empty ranges property implies 1-1 translation */ + if (ranges_count == 0) + return addr; + + for (i = 0; i < ranges_count; i++, ranges += stride) { + /* ranges format: <child base> <parent base> <size> */ + u64 child_base = dt_get_number(ranges, na); + u64 parent_base = dt_get_number(ranges + na, parent_na); + u64 size = dt_get_number(ranges + na + parent_na, ns); + + if (addr >= child_base && addr < child_base + size) + return (addr - child_base) + parent_base; + } + + /* input address was outside the any of our mapped ranges */ + return 0; +} + +u64 dt_translate_address(const struct dt_node *node, unsigned int index, + u64 *out_size) +{ + u64 addr = dt_get_address(node, index, NULL); + struct dt_node *bus = node->parent; + + /* FIXME: One day we will probably want to use this, but for now just + * force it it to be zero since we only support returning a u64 or u32 + */ + assert(!out_size); + + /* apply each translation until we hit the root bus */ + while (bus->parent) { + addr = dt_translate_one(bus, addr); + bus = bus->parent; + } + + return addr; +} + +bool dt_node_is_enabled(struct dt_node *node) +{ + const struct dt_property *p = dt_find_property(node, "status"); + + if (!p) + return true; + + return p->len > 1 && p->prop[0] == 'o' && p->prop[1] == 'k'; +} + +/* + * Function to fixup the phandle in the subtree. + */ +void dt_adjust_subtree_phandle(struct dt_node *dev, + const char** (get_properties_to_fix)(struct dt_node *n)) +{ + struct dt_node *node; + struct dt_property *prop; + u32 phandle, max_phandle = 0, import_phandle = new_phandle(); + __be32 p; + const char **name; + + dt_for_each_node(dev, node) { + const char **props_to_update; + node->phandle += import_phandle; + + /* + * calculate max_phandle(new_tree), needed to update + * last_phandle. + */ + if (node->phandle >= max_phandle) + max_phandle = node->phandle; + + props_to_update = get_properties_to_fix(node); + if (!props_to_update) + continue; + for (name = props_to_update; *name != NULL; name++) { + prop = __dt_find_property(node, *name); + if (!prop) + continue; + phandle = dt_prop_get_u32(node, *name); + phandle += import_phandle; + p = cpu_to_be32(phandle); + memcpy((char *)&prop->prop, &p, prop->len); + } + } + + set_last_phandle(max_phandle); +} |