1850 lines
		
	
	
		
			43 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1850 lines
		
	
	
		
			43 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/*
 | 
						||
 * 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 <rthw.h>
 | 
						||
#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 }, _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 <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) - 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;
 | 
						||
}
 |