2023-07-05 13:50:18 +08:00
|
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
|
|
|
|
|
2023-10-18 20:50:30 +08:00
|
|
|
|
|
2023-07-05 13:50:18 +08:00
|
|
|
|
#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);
|
2023-08-02 12:48:24 +08:00
|
|
|
|
rt_size_t tag_len = strchrnul(node_name, '@') - node_name;
|
2023-07-05 13:50:18 +08:00
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2023-08-02 12:48:24 +08:00
|
|
|
|
struct rt_ofw_node *np = RT_NULL, *parent, *tmp = RT_NULL;
|
2023-07-05 13:50:18 +08:00
|
|
|
|
|
|
|
|
|
if (path)
|
|
|
|
|
{
|
|
|
|
|
if (!rt_strcmp(path, "/"))
|
|
|
|
|
{
|
|
|
|
|
np = ofw_node_root;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
++path;
|
|
|
|
|
parent = rt_ofw_node_get(ofw_node_root);
|
|
|
|
|
|
|
|
|
|
while (*path)
|
|
|
|
|
{
|
2023-08-02 12:48:24 +08:00
|
|
|
|
const char *next = strchrnul(path, '/');
|
2023-07-05 13:50:18 +08:00
|
|
|
|
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 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);
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|