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;
 | ||
| }
 |