/* * Copyright (c) 2006-2024, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2022-08-25 GuEe-GUI first version */ #include #include #include #include #include #include #define DBG_TAG "rtdm.ofw" #define DBG_LVL DBG_INFO #include #include "ofw_internal.h" struct rt_ofw_node *ofw_node_root = RT_NULL; struct rt_ofw_node *ofw_node_cpus = RT_NULL; struct rt_ofw_node *ofw_node_chosen = RT_NULL; struct rt_ofw_node *ofw_node_aliases = RT_NULL; struct rt_ofw_node *ofw_node_reserved_memory = RT_NULL; static rt_phandle _phandle_range[2] = { 1, 1 }, _phandle_next = 1; static struct rt_ofw_node **_phandle_hash = RT_NULL; static rt_list_t _aliases_nodes = RT_LIST_OBJECT_INIT(_aliases_nodes); rt_err_t ofw_phandle_hash_reset(rt_phandle min, rt_phandle max) { rt_err_t err = RT_EOK; rt_phandle next = max; struct rt_ofw_node **hash_ptr = RT_NULL; max = RT_ALIGN(max, OFW_NODE_MIN_HASH); if (max > _phandle_range[1]) { rt_size_t size = sizeof(*_phandle_hash) * (max - min); if (!_phandle_hash) { hash_ptr = rt_calloc(1, size); } else { hash_ptr = rt_realloc(_phandle_hash, size); if (hash_ptr) { rt_size_t old_max = _phandle_range[1]; rt_memset(&hash_ptr[old_max], 0, sizeof(_phandle_hash) * (max - old_max)); } } } if (hash_ptr) { /* We always reset min value only once */ if (min) { _phandle_range[0] = min; } _phandle_range[1] = max; _phandle_next = next + 1; _phandle_hash = hash_ptr; } else { err = -RT_ENOMEM; } return err; } static rt_phandle ofw_phandle_next(void) { rt_phandle next; static struct rt_spinlock op_lock = {}; rt_hw_spin_lock(&op_lock.lock); RT_ASSERT(_phandle_next != OFW_PHANDLE_MAX); if (_phandle_next <= _phandle_range[1]) { next = _phandle_next++; } else { rt_err_t err = ofw_phandle_hash_reset(_phandle_range[0], _phandle_next); if (!err) { next = _phandle_next++; } else { next = 0; LOG_E("Expanded phandle hash[%u, %u] fail error = %s", _phandle_range[0], _phandle_next + 1, rt_strerror(err)); } } rt_hw_spin_unlock(&op_lock.lock); return next; } static void ofw_prop_destroy(struct rt_ofw_prop *prop) { struct rt_ofw_prop *next; while (prop) { next = prop->next; rt_free(prop); prop = next; } } static struct rt_ofw_node *ofw_get_next_node(struct rt_ofw_node *prev) { struct rt_ofw_node *np; /* * Walk: * * / { ------------------------ [0] (START) has child, goto child. * * node0 { ---------------- [1] has child, goto child. * * node0_0 { ---------- [2] no child, has sibling, goto sibling. * }; * * node0_1 { ---------- [3] no sibling now. * upward while the parent has sibling. * }; * }; * * node1 { ---------------- [4] come from node0 who find the sibling: * node1, node1 has child, goto child. * * node1_0 { ---------- [5] has child, goto child. * * node1_0_0 { ---- [6] no sibling now. * upward while the parent has sibling. * (END) in the root. * }; * }; * }; * }; */ if (!prev) { np = ofw_node_root; } else if (prev->child) { np = prev->child; } else { np = prev; while (np->parent && !np->sibling) { np = np->parent; } np = np->sibling; } return np; } static void ofw_node_destroy(struct rt_ofw_node *np) { struct rt_ofw_node *prev; if (np->parent) { /* Ask parent and prev sibling we are destroy. */ prev = np->parent->child; if (prev == np) { np->parent->child = RT_NULL; } else { while (prev->sibling != np) { prev = prev->sibling; } prev->sibling = np->sibling; } } while (np) { if (rt_ofw_node_test_flag(np, RT_OFW_F_SYSTEM) == RT_FALSE) { LOG_E("%s is system node", np->full_name); RT_ASSERT(0); } prev = np; np = ofw_get_next_node(np); ofw_prop_destroy(prev->props); rt_free(prev); } } rt_err_t rt_ofw_node_destroy(struct rt_ofw_node *np) { rt_err_t err = RT_EOK; if (np) { if (rt_ref_read(&np->ref) <= 1) { ofw_node_destroy(np); } else { err = -RT_EBUSY; } } else { err = -RT_EINVAL; } return err; } struct rt_ofw_node *rt_ofw_node_get(struct rt_ofw_node *np) { if (np) { LOG_D("%s get ref = %d", np->full_name, rt_ref_read(&np->ref)); rt_ref_get(&np->ref); } return np; } static void ofw_node_release(struct rt_ref *r) { struct rt_ofw_node *np = rt_container_of(r, struct rt_ofw_node, ref); LOG_E("%s is release", np->full_name); (void)np; RT_ASSERT(0); } void rt_ofw_node_put(struct rt_ofw_node *np) { if (np) { LOG_D("%s put ref = %d", np->full_name, rt_ref_read(&np->ref)); rt_ref_put(&np->ref, &ofw_node_release); } } rt_bool_t rt_ofw_node_tag_equ(const struct rt_ofw_node *np, const char *tag) { rt_bool_t ret = RT_FALSE; if (np && tag) { const char *node_name = rt_fdt_node_name(np->full_name); rt_size_t tag_len = strchrnul(node_name, '@') - node_name; ret = (rt_strlen(tag) == tag_len && !rt_strncmp(node_name, tag, tag_len)); } return ret; } rt_bool_t rt_ofw_node_tag_prefix(const struct rt_ofw_node *np, const char *prefix) { rt_bool_t ret = RT_FALSE; if (np && prefix) { ret = !rt_strncmp(rt_fdt_node_name(np->full_name), prefix, rt_strlen(prefix)); } return ret; } static int ofw_prop_index_of_string(struct rt_ofw_prop *prop, const char *string, rt_int32_t (*cmp)(const char *cs, const char *ct)) { int index = -1; rt_size_t len = prop->length, slen = 0; const char *value = prop->value; for (int idx = 0; len > 0; ++idx) { /* Add '\0' */ slen = rt_strlen(value) + 1; if (!cmp(value, string)) { index = idx; break; } len -= slen; value += slen; } return index; } static rt_int32_t ofw_strcasecmp(const char *cs, const char *ct) { extern rt_int32_t strcasecmp(const char *cs, const char *ct); return rt_strcasecmp(cs, ct); } static int ofw_prop_index_of_compatible(struct rt_ofw_prop *prop, const char *compatible) { return ofw_prop_index_of_string(prop, compatible, ofw_strcasecmp); } static int ofw_node_index_of_compatible(const struct rt_ofw_node *np, const char *compatible) { int idx = -1; struct rt_ofw_prop *prop = rt_ofw_get_prop(np, "compatible", RT_NULL); if (prop) { idx = ofw_prop_index_of_compatible(prop, compatible); } return idx; } rt_bool_t rt_ofw_machine_is_compatible(const char *compatible) { return ofw_node_index_of_compatible(ofw_node_root, compatible) >= 0; } /* * Property status: * * "okay" or "ok": * Indicates the device is operational. * * "disabled": * Indicates that the device is not presently operational, but it might * become operational in the future (for example, something is not * plugged in, or switched off). * Refer to the device binding for details on what disabled means for a * given device. * * "reserved": * Indicates that the device is operational, but should not be used. * Typically this is used for devices that are controlled by another * software component, such as platform firmware. * * "fail": * Indicates that the device is not operational. A serious error was * detected in the device, and it is unlikely to become operational * without repair. * * "fail-sss": * Indicates that the device is not operational. A serious error was * detected in the device and it is unlikely to become operational * without repair. The sss portion of the value is specific to the * device and indicates the error condition detected. */ static rt_bool_t ofw_node_is_fail(const struct rt_ofw_node *np) { rt_bool_t res = RT_FALSE; const char *status = rt_ofw_prop_read_raw(np, "status", RT_NULL); if (status) { res = !rt_strcmp(status, "fail") || !rt_strncmp(status, "fail-", 5); } return res; } static rt_bool_t ofw_node_is_available(const struct rt_ofw_node *np) { rt_bool_t res = RT_TRUE; const char *status = rt_ofw_prop_read_raw(np, "status", RT_NULL); if (status) { res = !rt_strcmp(status, "okay") || !rt_strcmp(status, "ok"); } return res; } rt_bool_t rt_ofw_node_is_available(const struct rt_ofw_node *np) { return np ? ofw_node_is_available(np) : RT_FALSE; } rt_bool_t rt_ofw_node_is_compatible(const struct rt_ofw_node *np, const char *compatible) { rt_bool_t res = RT_FALSE; if (np) { res = ofw_node_index_of_compatible(np, compatible) >= 0; } return res; } static struct rt_ofw_node_id *ofw_prop_match(struct rt_ofw_prop *prop, const struct rt_ofw_node_id *ids) { int best_index = RT_UINT32_MAX >> 1, index; struct rt_ofw_node_id *found_id = RT_NULL, *id; for (id = (struct rt_ofw_node_id *)ids; id->compatible[0]; ++id) { index = ofw_prop_index_of_compatible(prop, id->compatible); if (index >= 0 && index < best_index) { found_id = id; best_index = index; } } return found_id; } struct rt_ofw_node_id *rt_ofw_prop_match(struct rt_ofw_prop *prop, const struct rt_ofw_node_id *ids) { struct rt_ofw_node_id *id = RT_NULL; if (prop && ids && !rt_strcmp(prop->name, "compatible")) { id = ofw_prop_match(prop, ids); } return id; } struct rt_ofw_node_id *rt_ofw_node_match(struct rt_ofw_node *np, const struct rt_ofw_node_id *ids) { struct rt_ofw_prop *prop; struct rt_ofw_node_id *id = RT_NULL; if (np && ids && (prop = rt_ofw_get_prop(np, "compatible", RT_NULL))) { id = ofw_prop_match(prop, ids); } return id; } struct rt_ofw_node *rt_ofw_find_node_by_tag(struct rt_ofw_node *from, const char *tag) { struct rt_ofw_node *np = RT_NULL; if (tag) { rt_ofw_foreach_nodes(from, np) { if (rt_ofw_node_tag_equ(np, tag)) { break; } } } return np; } struct rt_ofw_node *rt_ofw_find_node_by_prop_r(struct rt_ofw_node *from, const char *propname, const struct rt_ofw_prop **out_prop) { struct rt_ofw_node *np = RT_NULL; if (propname) { rt_ofw_foreach_nodes(from, np) { struct rt_ofw_prop *prop = rt_ofw_get_prop(np, propname, RT_NULL); if (prop) { if (out_prop) { *out_prop = prop; } break; } } } return np; } struct rt_ofw_node *rt_ofw_find_node_by_name(struct rt_ofw_node *from, const char *name) { struct rt_ofw_node *np = RT_NULL; if (name) { rt_ofw_foreach_nodes(from, np) { if (np->name && !rt_strcmp(np->name, name)) { np = rt_ofw_node_get(np); break; } } } return np; } struct rt_ofw_node *rt_ofw_find_node_by_type(struct rt_ofw_node *from, const char *type) { struct rt_ofw_node *np = RT_NULL; if (type) { rt_ofw_foreach_nodes(from, np) { if (rt_ofw_node_is_type(np, type)) { break; } } } return np; } struct rt_ofw_node *rt_ofw_find_node_by_compatible(struct rt_ofw_node *from, const char *compatible) { struct rt_ofw_node *np = RT_NULL; if (compatible) { rt_ofw_foreach_nodes(from, np) { if (ofw_node_index_of_compatible(np, compatible) >= 0) { break; } } } return np; } struct rt_ofw_node *rt_ofw_find_node_by_ids_r(struct rt_ofw_node *from, const struct rt_ofw_node_id *ids, const struct rt_ofw_node_id **out_id) { struct rt_ofw_node *np = RT_NULL; if (ids) { rt_ofw_foreach_nodes(from, np) { struct rt_ofw_node_id *id = rt_ofw_node_match(np, ids); if (id) { if (out_id) { *out_id = id; } break; } } } return np; } struct rt_ofw_node *rt_ofw_find_node_by_path(const char *path) { struct rt_ofw_node *np = RT_NULL, *parent, *tmp = RT_NULL; if (path) { if (!rt_strcmp(path, "/")) { np = ofw_node_root; } else { ++path; parent = rt_ofw_node_get(ofw_node_root); while (*path) { const char *next = strchrnul(path, '/'); rt_size_t len = next - path; tmp = RT_NULL; rt_ofw_foreach_child_node(parent, np) { if (!rt_strncmp(np->full_name, path, len)) { rt_ofw_node_put(parent); parent = np; tmp = np; break; } } if (!tmp) { rt_ofw_node_put(parent); break; } path += len + !!*next; } np = tmp; } rt_ofw_node_get(np); } return np; } struct rt_ofw_node *rt_ofw_find_node_by_phandle(rt_phandle phandle) { struct rt_ofw_node *np = RT_NULL; if (phandle >= OFW_PHANDLE_MIN && phandle <= OFW_PHANDLE_MAX) { /* rebase from zero */ rt_phandle poff = phandle - _phandle_range[0]; np = _phandle_hash[poff]; if (!np) { rt_ofw_foreach_allnodes(np) { if (np->phandle == phandle) { _phandle_hash[poff] = np; break; } } } else { rt_ofw_node_get(np); } } return np; } struct rt_ofw_node *rt_ofw_get_parent(const struct rt_ofw_node *np) { if (np) { np = rt_ofw_node_get(np->parent); } return (struct rt_ofw_node *)np; } struct rt_ofw_node *rt_ofw_get_child_by_tag(const struct rt_ofw_node *parent, const char *tag) { struct rt_ofw_node *child = RT_NULL; if (parent && tag) { rt_ofw_foreach_child_node(parent, child) { if (rt_ofw_node_tag_equ(child, tag)) { break; } } } return child; } struct rt_ofw_node *rt_ofw_get_child_by_compatible(const struct rt_ofw_node *parent, const char *compatible) { struct rt_ofw_node *child = RT_NULL; if (parent && compatible) { rt_ofw_foreach_child_node(parent, child) { if (ofw_node_index_of_compatible(child, compatible) >= 0) { break; } } } return child; } int rt_ofw_get_child_count(const struct rt_ofw_node *np) { int nr; if (np) { struct rt_ofw_node *child; nr = 0; rt_ofw_foreach_child_node(np, child) { ++nr; } } else { nr = -RT_EINVAL; } return nr; } int rt_ofw_get_available_child_count(const struct rt_ofw_node *np) { int nr; if (np) { struct rt_ofw_node *child; nr = 0; rt_ofw_foreach_available_child_node(np, child) { ++nr; } } else { nr = -RT_EINVAL; } return nr; } struct rt_ofw_node *rt_ofw_get_next_node(struct rt_ofw_node *prev) { struct rt_ofw_node *np; np = rt_ofw_node_get(ofw_get_next_node(prev)); rt_ofw_node_put(prev); return np; } struct rt_ofw_node *rt_ofw_get_next_parent(struct rt_ofw_node *prev) { struct rt_ofw_node *next = RT_NULL; if (prev) { next = rt_ofw_node_get(prev->parent); rt_ofw_node_put(prev); } return next; } struct rt_ofw_node *rt_ofw_get_next_child(const struct rt_ofw_node *parent, struct rt_ofw_node *prev) { struct rt_ofw_node *next = RT_NULL; if (parent) { next = prev ? prev->sibling : parent->child; rt_ofw_node_put(prev); rt_ofw_node_get(next); } return next; } struct rt_ofw_node *rt_ofw_get_next_available_child(const struct rt_ofw_node *parent, struct rt_ofw_node *prev) { struct rt_ofw_node *next = RT_NULL; if (parent) { next = prev; do { next = rt_ofw_get_next_child(parent, next); } while (next && !ofw_node_is_available(next)); } return next; } struct rt_ofw_node *rt_ofw_get_cpu_node(int cpu, int *thread, rt_bool_t (*match_cpu_hwid)(int cpu, rt_uint64_t hwid)) { const char *propname = "reg"; struct rt_ofw_node *cpu_np = RT_NULL; /* * "reg" (some of the obsolete arch may be other names): * The value of reg is a that defines a unique * CPU/thread id for the CPU/threads represented by the CPU node. * * If a CPU supports more than one thread (i.e. multiple streams of * execution) the reg property is an array with 1 element per thread. The * #address-cells on the /cpus node specifies how many cells each element * of the array takes. Software can determine the number of threads by * dividing the size of reg by the parent node’s #address-cells: * * thread-number = reg-cells / address-cells * * If a CPU/thread can be the target of an external interrupt the reg * property value must be a unique CPU/thread id that is addressable by the * interrupt controller. * * If a CPU/thread cannot be the target of an external interrupt, then reg * must be unique and out of bounds of the range addressed by the interrupt * controller * * If a CPU/thread’s PIR (pending interrupt register) is modifiable, a * client program should modify PIR to match the reg property value. If PIR * cannot be modified and the PIR value is distinct from the interrupt * controller number space, the CPUs binding may define a binding-specific * representation of PIR values if desired. */ rt_ofw_foreach_cpu_node(cpu_np) { rt_ssize_t prop_len; rt_bool_t is_end = RT_FALSE; int tid, addr_cells = rt_ofw_io_addr_cells(cpu_np); const fdt32_t *cell = rt_ofw_prop_read_raw(cpu_np, propname, &prop_len); if (!cell && !addr_cells) { if (match_cpu_hwid && match_cpu_hwid(cpu, 0)) { break; } continue; } if (!match_cpu_hwid) { continue; } prop_len /= sizeof(*cell) * addr_cells; for (tid = 0; tid < prop_len; ++tid) { rt_uint64_t hwid = rt_fdt_read_number(cell, addr_cells); if (match_cpu_hwid(cpu, hwid)) { if (thread) { *thread = tid; } is_end = RT_TRUE; break; } cell += addr_cells; } if (is_end) { break; } } return cpu_np; } struct rt_ofw_node *rt_ofw_get_next_cpu_node(struct rt_ofw_node *prev) { struct rt_ofw_node *cpu_np; if (prev) { cpu_np = prev->sibling; rt_ofw_node_put(prev); } else { cpu_np = ofw_node_cpus->child; } for (; cpu_np; cpu_np = cpu_np->sibling) { if (ofw_node_is_fail(cpu_np)) { continue; } if (!(rt_ofw_node_tag_equ(cpu_np, "cpu") || rt_ofw_node_is_type(cpu_np, "cpu"))) { continue; } if (rt_ofw_node_get(cpu_np)) { break; } } return cpu_np; } struct rt_ofw_node *rt_ofw_get_cpu_state_node(struct rt_ofw_node *cpu_np, int index) { struct rt_ofw_cell_args args; struct rt_ofw_node *np = RT_NULL, *state_np; rt_err_t err = rt_ofw_parse_phandle_cells(cpu_np, "power-domains", "#power-domain-cells", 0, &args); if (!err) { state_np = rt_ofw_parse_phandle(args.data, "domain-idle-states", index); rt_ofw_node_put(args.data); if (state_np) { np = state_np; } } if (!np) { int count = 0; rt_uint32_t phandle; const fdt32_t *cell; struct rt_ofw_prop *prop; rt_ofw_foreach_prop_u32(cpu_np, "cpu-idle-states", prop, cell, phandle) { if (count == index) { np = rt_ofw_find_node_by_phandle((rt_phandle)phandle); break; } ++count; } } return np; } rt_uint64_t rt_ofw_get_cpu_id(struct rt_ofw_node *cpu_np) { rt_uint64_t cpuid = ~0ULL; if (cpu_np) { rt_uint64_t idx = 0; struct rt_ofw_node *np = ofw_node_cpus->child; for (; np; np = np->sibling) { if (!(rt_ofw_node_tag_equ(cpu_np, "cpu") || rt_ofw_node_is_type(cpu_np, "cpu"))) { continue; } if (cpu_np == np) { cpuid = idx; break; } ++idx; } if ((rt_int64_t)cpuid < 0 && !rt_ofw_prop_read_u64(cpu_np, "rt-thread,cpuid", &idx)) { cpuid = idx; } } return cpuid; } rt_uint64_t rt_ofw_get_cpu_hwid(struct rt_ofw_node *cpu_np, unsigned int thread) { rt_uint64_t thread_id, hwid = ~0ULL; if (cpu_np && thread >= 0 && !rt_ofw_get_address(cpu_np, thread, &thread_id, RT_NULL)) { hwid = thread_id; } return hwid; } rt_err_t ofw_alias_scan(void) { rt_err_t err = RT_EOK; struct rt_ofw_prop *prop; struct rt_ofw_node *np = ofw_node_aliases, *tmp; rt_ofw_foreach_prop(np, prop) { int id = 0, rate = 1; struct alias_info *info; const char *name = prop->name, *end; /* Maybe the bootloader will set the name, or other nodes reference the aliases */ if (!rt_strcmp(name, "name") || !rt_strcmp(name, "phandle")) { continue; } if (!(tmp = rt_ofw_find_node_by_path(prop->value))) { continue; } end = name + rt_strlen(name) - 1; while (*end && !(*end >= '0' && *end <= '9') && end > name) { --end; } while (*end && (*end >= '0' && *end <= '9')) { id += (*end - '0') * rate; rate *= 10; ++end; } info = rt_malloc(sizeof(*info)); if (!info) { err = -RT_ENOMEM; break; } rt_list_init(&info->list); info->id = id; info->tag = name; info->tag_len = end - name - 1; info->np = tmp; rt_list_insert_after(&_aliases_nodes, &info->list); } return err; } struct rt_ofw_node *rt_ofw_get_alias_node(const char *tag, int id) { struct alias_info *info; struct rt_ofw_node *np = RT_NULL; if (tag && id >= 0) { if (!rt_list_isempty(&_aliases_nodes)) { rt_list_for_each_entry(info, &_aliases_nodes, list) { if (rt_strncmp(info->tag, tag, info->tag_len)) { continue; } if (info->id == id) { np = info->np; break; } } } } return np; } int ofw_alias_node_id(struct rt_ofw_node *np) { int id; struct alias_info *info = RT_NULL; if (np) { id = -1; if (!rt_list_isempty(&_aliases_nodes)) { rt_list_for_each_entry(info, &_aliases_nodes, list) { if (info->np == np) { id = info->id; break; } } } } else { id = -RT_EINVAL; } return id; } int rt_ofw_get_alias_id(struct rt_ofw_node *np, const char *tag) { int id; struct alias_info *info; if (np && tag) { id = -1; if (!rt_list_isempty(&_aliases_nodes)) { rt_list_for_each_entry(info, &_aliases_nodes, list) { if (rt_strncmp(info->tag, tag, info->tag_len)) { continue; } if (info->np == np) { id = info->id; break; } } } } else { id = -RT_EINVAL; } return id; } int rt_ofw_get_alias_last_id(const char *tag) { int id; struct alias_info *info; if (tag) { id = -1; if (!rt_list_isempty(&_aliases_nodes)) { rt_list_for_each_entry(info, &_aliases_nodes, list) { if (rt_strncmp(info->tag, tag, info->tag_len)) { continue; } if (info->id > id) { id = info->id; } } } } else { id = -RT_EINVAL; } return id; } struct rt_ofw_node *rt_ofw_append_child(struct rt_ofw_node *parent, const char *full_name) { rt_phandle phandle; rt_err_t err = RT_EOK; fdt32_t *phandle_value; struct rt_ofw_node *np = RT_NULL, *child; if (full_name) { if ((phandle = ofw_phandle_next())) { np = rt_calloc(1, sizeof(*np) + sizeof(*phandle_value)); } } if (np) { parent = parent ? : ofw_node_root; np->full_name = full_name; np->phandle = phandle; np->parent = parent; rt_ref_init(&np->ref); phandle_value = (void *)np + sizeof(*np); *phandle_value = cpu_to_fdt32(phandle); err = rt_ofw_append_prop(np, "phandle", sizeof(*phandle_value), phandle_value); if (!err) { if (parent->child) { rt_ofw_foreach_child_node(parent, child) { if (!child->sibling) { child->sibling = np; rt_ofw_node_put(child); break; } } } else { parent->child = np; } } else { rt_free(np); np = RT_NULL; } } return np; } rt_err_t rt_ofw_append_prop(struct rt_ofw_node *np, const char *name, int length, void *value) { rt_err_t err = RT_EOK; if (np && name && ((length && value) || (!length && !value))) { struct rt_ofw_prop *prop = rt_malloc(sizeof(*prop)), *last_prop; if (prop) { prop->name = name; prop->length = length; prop->value = value; prop->next = RT_NULL; if (np->props) { rt_ofw_foreach_prop(np, last_prop) { if (!last_prop->next) { last_prop->next = prop; break; } } } else { np->props = prop; } } else { err = -RT_ENOMEM; } } else { err = -RT_EINVAL; } return err; } struct rt_ofw_node *rt_ofw_parse_phandle(const struct rt_ofw_node *np, const char *phandle_name, int index) { struct rt_ofw_cell_args args; struct rt_ofw_node *ref_np = RT_NULL; if (!rt_ofw_parse_phandle_cells(np, phandle_name, RT_NULL, index, &args)) { ref_np = args.data; } return ref_np; } static rt_err_t ofw_parse_phandle_cells(const struct rt_ofw_node *np, const char *list_name, const char *cells_name, int index, struct rt_ofw_cell_args *out_args) { rt_err_t err = -RT_EEMPTY; rt_uint32_t value; rt_size_t count = 0; const fdt32_t *cell; struct rt_ofw_prop *prop; /* * List: * * phandle1: node1 { * #list-cells = <2>; * }; * * phandle2: node2 { * #list-cells = <1>; * }; * * node3 { * list = <&phandle1 0xaa 0xbb>, <&phandle2 0xcc>; * }; * * if call: * rt_ofw_parse_phandle_cells(node3, "list", "#list-cells", 0, &args): * * args.data = node1; * args.args_count = 2; * args.args[0] = 0xaa; * args.args[1] = 0xbb; * * rt_ofw_parse_phandle_cells(node3, "list", "#list-cells", 1, &args): * * args.data = node2; * args.args_count = 1; * args.args[0] = 0xcc; */ rt_ofw_foreach_prop_u32(np, list_name, prop, cell, value) { rt_uint32_t cells_count = 0; struct rt_ofw_node *phandle_np = rt_ofw_find_node_by_phandle((rt_phandle)value); /* if phandle node is undefined, we assume that the cels_count is 0 */ if (cells_name && phandle_np) { rt_ofw_prop_read_u32(phandle_np, cells_name, &cells_count); } if (count++ == index) { for (int idx = 0; idx < cells_count; ++idx) { cell = rt_ofw_prop_next_u32(prop, cell, &value); out_args->args[idx] = value; } out_args->args_count = cells_count; out_args->data = phandle_np; if (out_args->data) { err = RT_EOK; } break; } cell += cells_count; } return err; } rt_err_t rt_ofw_parse_phandle_cells(const struct rt_ofw_node *np, const char *list_name, const char *cells_name, int index, struct rt_ofw_cell_args *out_args) { rt_err_t err; if (np && list_name && index >= 0 && out_args) { err = ofw_parse_phandle_cells(np, list_name, cells_name, index, out_args); } else { err = -RT_EINVAL; } return err; } int rt_ofw_count_phandle_cells(const struct rt_ofw_node *np, const char *list_name, const char *cells_name) { int count; if (np && list_name) { count = -1; if (!cells_name) { rt_ssize_t length; if (rt_ofw_get_prop(np, list_name, &length)) { count = length / sizeof(fdt32_t); } } else { int index = count = 0; struct rt_ofw_cell_args args; while (!ofw_parse_phandle_cells(np, list_name, cells_name, index, &args)) { ++index; ++count; } } } else { count = -RT_EINVAL; } return count; } static const char *ofw_get_prop_fuzzy_name(const struct rt_ofw_node *np, const char *name) { char *sf, split_field[64]; rt_size_t len = 0, max_ak = 0; const char *str, *result = RT_NULL; RT_BITMAP_DECLARE(ak, sizeof(split_field)) = {0}; struct rt_ofw_prop *prop; /* * List: * * node { * property; * front-prop-rear; * front-prop; * prop-rear; * }; * * if call: * ofw_get_prop_fuzzy_name(node, name): * ["prop"] => property * ["-prop"] => front-prop-rear * ["prop-"] => front-prop-rear * ["-prop$"] => front-prop * ["^prop-"] => prop-rear * ["-prop-"] => front-prop-rear * ["front-*-rear"] => front-prop-rear */ str = name; sf = split_field; if (str[0] != '^') { /* As '*' */ *sf++ = '\0'; rt_bitmap_set_bit(ak, len++); } else { ++str; } for (; *str && len < sizeof(split_field); ++str, ++sf, ++len) { if (*str != '*') { *sf = *str; rt_bitmap_clear_bit(ak, len); } else { max_ak = len; *sf = '\0'; rt_bitmap_set_bit(ak, len); } } *sf = '\0'; if (str[-1] != '$') { /* As '*' */ max_ak = len; rt_bitmap_set_bit(ak, len++); } else { sf[-1] = '\0'; --len; } sf = split_field; if (len >= sizeof(split_field)) { LOG_W("%s fuzzy name = %s len is %d out of %d", np->full_name, name, rt_strlen(name), sizeof(split_field)); } rt_ofw_foreach_prop(np, prop) { int prep_ak = 0, next_ak, field; rt_bool_t match = RT_TRUE; const char *propname = prop->name, *fuzzy_name = sf; if (!rt_bitmap_test_bit(ak, prep_ak)) { next_ak = rt_bitmap_next_set_bit(ak, prep_ak + 1, max_ak) ? : len; field = next_ak - prep_ak; if (rt_strncmp(propname, fuzzy_name, field)) { continue; } propname += field; fuzzy_name += field; prep_ak = next_ak; } rt_bitmap_for_each_set_bit_from(ak, prep_ak, next_ak, max_ak) { /* Skip the '*' */ if (prep_ak == next_ak) { ++fuzzy_name; next_ak = rt_bitmap_next_set_bit(ak, prep_ak + 1, max_ak); } if (!(str = rt_strstr(propname, fuzzy_name))) { match = RT_FALSE; break; } field = next_ak - prep_ak; propname = str + field - 1; fuzzy_name += field; prep_ak = next_ak; } if (match) { if ((max_ak || !split_field[0]) && next_ak >= max_ak && len - max_ak > 1) { if (next_ak == max_ak) { /* Skip the last '*' */ ++fuzzy_name; } if (!(propname = rt_strstr(propname, fuzzy_name))) { continue; } /* Check end flag */ if (propname[len - max_ak - 1] != '\0') { continue; } } result = prop->name; break; } } return result; } const char *rt_ofw_get_prop_fuzzy_name(const struct rt_ofw_node *np, const char *name) { const char *propname = RT_NULL; if (np && name) { propname = ofw_get_prop_fuzzy_name(np, name); } return propname; } struct rt_ofw_prop *rt_ofw_get_prop(const struct rt_ofw_node *np, const char *name, rt_ssize_t *out_length) { struct rt_ofw_prop *prop = RT_NULL; if (np && name) { rt_ofw_foreach_prop(np, prop) { if (!rt_strcmp(prop->name, name)) { if (out_length) { *out_length = prop->length; } break; } } } return prop; } #define OFW_PROP_READ_UXX_ARRAY_INDEX(bit) \ int rt_ofw_prop_read_u##bit##_array_index( \ const struct rt_ofw_node *np, const char *propname, \ int index, int nr, rt_uint##bit##_t *out_values) \ { \ int res, max_nr; \ if (np && propname && index >= 0 && nr >= 0 && out_values) \ { \ rt_ssize_t len; \ const fdt##bit##_t *elm; \ elm = rt_ofw_prop_read_raw(np, propname, &len); \ max_nr = len / sizeof(*elm); \ if (elm && index < max_nr) \ { \ elm += index; \ max_nr -= index; \ res = nr > max_nr ? max_nr : nr; \ for (nr = 0; nr < res; ++nr) \ { \ *out_values++ = fdt##bit##_to_cpu(*elm++); \ } \ } \ else \ { \ res = -RT_EEMPTY; \ } \ } \ else \ { \ res = -RT_EINVAL; \ } \ return res; \ } OFW_PROP_READ_UXX_ARRAY_INDEX(8) OFW_PROP_READ_UXX_ARRAY_INDEX(16) OFW_PROP_READ_UXX_ARRAY_INDEX(32) OFW_PROP_READ_UXX_ARRAY_INDEX(64) #undef OFW_PROP_READ_UXX_ARRAY_INDEX int rt_ofw_prop_read_string_array_index(const struct rt_ofw_node *np, const char *propname, int index, int nr, const char **out_strings) { int res = 0; if (np && propname && index >= 0 && nr >= 0 && out_strings) { rt_ssize_t len, slen = 0; const char *value = rt_ofw_prop_read_raw(np, propname, &len); if (value) { nr += index; for (int idx = 0; idx < nr && len > 0; ++idx) { /* Add '\0' */ slen = rt_strlen(value) + 1; if (idx >= index) { *out_strings++ = value; ++res; } len -= slen; value += slen; } } else { res = -RT_EEMPTY; } } else { res = -RT_EINVAL; } return res; } int rt_ofw_prop_count_of_size(const struct rt_ofw_node *np, const char *propname, int size) { int count; if (np && propname && size > 0) { rt_ssize_t len; count = -RT_EEMPTY; if (rt_ofw_get_prop(np, propname, &len)) { count = len / size; } } else { count = -RT_EINVAL; } return count; } static rt_int32_t ofw_strcmp(const char *cs, const char *ct) { return rt_strcmp(cs, ct); } int rt_ofw_prop_index_of_string(const struct rt_ofw_node *np, const char *propname, const char *string) { int idx; if (np && propname && string) { struct rt_ofw_prop *prop = rt_ofw_get_prop(np, propname, RT_NULL); idx = -1; if (prop) { idx = ofw_prop_index_of_string(prop, string, ofw_strcmp); } } else { idx = -RT_EINVAL; } return idx; } const fdt32_t *rt_ofw_prop_next_u32(struct rt_ofw_prop *prop, const fdt32_t *cur, rt_uint32_t *out_value) { if (prop && out_value) { if (cur) { ++cur; if ((void *)cur >= prop->value + prop->length) { cur = RT_NULL; } } else { cur = prop->value; } if (cur) { *out_value = fdt32_to_cpu(*cur); } } else { cur = RT_NULL; } return cur; } const char *rt_ofw_prop_next_string(struct rt_ofw_prop *prop, const char *cur) { if (prop) { if (cur) { cur += rt_strlen(cur) + 1; if ((void *)cur >= prop->value + prop->length) { cur = RT_NULL; } } else { cur = prop->value; } } else { cur = RT_NULL; } return cur; }