rt-thread/components/drivers/ofw/base.c

1514 lines
33 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-08-25 GuEe-GUI first version
*/
#define _GNU_SOURCE
#include <rtthread.h>
#include <drivers/ofw.h>
#include <drivers/ofw_io.h>
#include <drivers/ofw_fdt.h>
#include <drivers/ofw_raw.h>
#define DBG_TAG "rtdm.ofw"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#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 };
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;
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_hash = hash_ptr;
}
else
{
err = -RT_ENOMEM;
}
return err;
}
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);
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;
}
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 <prop-encoded-array> 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 nodes #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/threads 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);
while (*end && !(*end >= '0' && *end <= '9'))
{
--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;
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)
{
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 rt_ofw_get_alias_id(struct rt_ofw_node *np, const char *tag)
{
int id;
struct alias_info *info;
if (np && tag)
{
id = -1;
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;
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_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;
}
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;
}