import RT-Thread@9217865c without bsp, libcpu and components/net
This commit is contained in:
commit
e2376a3709
1414 changed files with 390370 additions and 0 deletions
41
components/drivers/fdt/README.md
Normal file
41
components/drivers/fdt/README.md
Normal file
|
@ -0,0 +1,41 @@
|
|||
# fdt
|
||||
|
||||
## 1、介绍
|
||||
fdt基于libfdt进行封装,可实现在内存或文件系统中加载dtb设备树,对内存中的设备树修改、解析,并转换为设备节点树,通过该节点树开发者可通过设备树信息开发驱动。
|
||||
|
||||
### 1.1 目录结构
|
||||
| 名称 | 说明 |
|
||||
| ---- | ---- |
|
||||
| docs | 文档目录 |
|
||||
| examples | 例子目录,并有相应的一些说明 |
|
||||
| inc | 头文件目录 |
|
||||
| src | 源代码目录 |
|
||||
|
||||
### 1.2 许可证
|
||||
fdt package 遵循 GPL-3.0 许可,详见 LICENSE 文件。
|
||||
|
||||
### 1.3 依赖
|
||||
- RT-Thread 3.0+
|
||||
|
||||
## 2、如何打开 fdt
|
||||
使用 fdt package 需要在 RT-Thread 的包管理器中选择它,具体路径如下:
|
||||
|
||||
```
|
||||
RT-Thread online packages
|
||||
tools packages --->
|
||||
[*] Device Tree package in RT-Thread
|
||||
```
|
||||
|
||||
## 3、使用 fdt
|
||||
在打开 fdt package 后,当进行 bsp 编译时,它会被加入到 bsp 工程中进行编译。
|
||||
|
||||
* 完整的 API 手册可以访问这个[链接](docs/api.md)
|
||||
* 更多文档位于 [`/docs`](/docs) 下,使用前 **务必查看**
|
||||
|
||||
## 4、注意事项
|
||||
如果发生`libfdt`库冲突,在package管理菜单中取消选择`Enable libfdt`
|
||||
|
||||
## 5、联系方式
|
||||
|
||||
* 维护:GuEe-GUI
|
||||
* 主页:https://github.com/GuEe-GUI/fdt
|
16
components/drivers/fdt/SConscript
Normal file
16
components/drivers/fdt/SConscript
Normal file
|
@ -0,0 +1,16 @@
|
|||
# RT-Thread building script for bridge
|
||||
|
||||
import os
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
objs = []
|
||||
list = os.listdir(cwd)
|
||||
|
||||
if GetDepend('RT_USING_FDT'):
|
||||
for d in list:
|
||||
path = os.path.join(cwd, d)
|
||||
if os.path.isfile(os.path.join(path, 'SConscript')):
|
||||
objs = objs + SConscript(os.path.join(d, 'SConscript'))
|
||||
|
||||
Return('objs')
|
13
components/drivers/fdt/docs/README.md
Normal file
13
components/drivers/fdt/docs/README.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
# 文档
|
||||
|
||||
## 软件包地址
|
||||
|
||||
- https://gitee.com/GuEe_GUI/fdt
|
||||
|
||||
## 文档列表
|
||||
|
||||
|文件名 |描述|
|
||||
|:----- |:----|
|
||||
|[examples.md](examples.md) |示例程序|
|
||||
|[version.md](version.md) |版本信息|
|
||||
|[api.md](api.md) |API 说明|
|
434
components/drivers/fdt/docs/api.md
Normal file
434
components/drivers/fdt/docs/api.md
Normal file
|
@ -0,0 +1,434 @@
|
|||
# fdt load API
|
||||
|
||||
## 从文件系统上读取设备树
|
||||
```c
|
||||
void *fdt_load_from_fs(char *dtb_filename)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|dtb_filename | 设备树文件名 |
|
||||
| **返回** | **描述** |
|
||||
|void* | 设备树在内存上的地址 |
|
||||
|
||||
## 从内存上读取设备树
|
||||
```c
|
||||
void *fdt_load_from_memory(void *dtb_ptr, rt_bool_t is_clone)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|dtb_ptr | 设备树在内存上的内存地址 |
|
||||
|is_clone | 是否克隆到新的内存区域 |
|
||||
| **返回** | **描述** |
|
||||
|void* | 设备树在内存上的地址 |
|
||||
|
||||
# fdt set API
|
||||
|
||||
## 设置Linux启动参数
|
||||
```c
|
||||
rt_size_t fdt_set_linux_cmdline(void *fdt, char *cmdline)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|fdt | 设备树指针 |
|
||||
|cmdline | 启动参数 |
|
||||
| **返回** | **描述** |
|
||||
|rt_size_t | 修改设备树后设备树的大小 |
|
||||
|
||||
## 设置Linux init ramdisk
|
||||
```c
|
||||
rt_size_t fdt_set_linux_initrd(void *fdt, rt_uint64_t initrd_addr, rt_size_t initrd_size)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|fdt | 设备树指针 |
|
||||
|initrd_addr | init ramdisk 内存地址 |
|
||||
|initrd_size | init 大小 |
|
||||
| **返回** | **描述** |
|
||||
|rt_size_t | 修改设备树后设备树的大小 |
|
||||
|
||||
## 设置节点属性值
|
||||
```c
|
||||
rt_size_t fdt_set_dtb_property(void *fdt, char *pathname, char *property_name, rt_uint32_t *cells, rt_size_t cells_size);
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|fdt | 设备树指针 |
|
||||
|pathname | 节点路径 |
|
||||
|property_name | 属性名称 |
|
||||
|cells | 属性值地址 |
|
||||
|cells_size | 属性值长度 |
|
||||
| **返回** | **描述** |
|
||||
|rt_size_t | 修改设备树后设备树的大小 |
|
||||
|
||||
## 添加保留内存
|
||||
```c
|
||||
rt_size_t fdt_add_dtb_memreserve(void *fdt, rt_uint64_t address, rt_uint64_t size)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|fdt | 设备树指针 |
|
||||
|address | 保留内存起始地址 |
|
||||
|size | 保留内存大小 |
|
||||
| **返回** | **描述** |
|
||||
|rt_size_t | 修改设备树后设备树的大小 |
|
||||
|
||||
## 删除保留内存
|
||||
```c
|
||||
rt_size_t fdt_del_dtb_memreserve(void *fdt, rt_uint64_t address)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|fdt | 设备树指针 |
|
||||
|address | 保留内存起始地址 |
|
||||
| **返回** | **描述** |
|
||||
|rt_size_t | 修改设备树后设备树的大小 |
|
||||
|
||||
# fdt get API
|
||||
|
||||
## 获取设备树软件包执行状态
|
||||
```c
|
||||
rt_err_t fdt_get_exec_status()
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|无 | 无参数 |
|
||||
| **返回** | **描述** |
|
||||
|FDT_RET_NO_MEMORY | 内存不足 |
|
||||
|FDT_RET_NO_LOADED | 设备树还未加载进内存 |
|
||||
|FDT_RET_GET_OK | 执行成功 |
|
||||
|FDT_RET_GET_EMPTY | 读取数据为空 |
|
||||
|
||||
## 将原始设备树转换为设备节点树
|
||||
```c
|
||||
struct dtb_node *fdt_get_dtb_list(void *fdt)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|fdt | 设备树在内存上的地址 |
|
||||
| **返回** | **描述** |
|
||||
|struct dtb_node * | 设备节点树头指针 |
|
||||
## 释放设备节点树内存
|
||||
```c
|
||||
void fdt_free_dtb_list(struct dtb_node *dtb_node_head)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|dtb_node_head | 设备节点树头节点 |
|
||||
| **返回** | **描述** |
|
||||
|无返回值 | 无描述 |
|
||||
|
||||
示例:加载设备树
|
||||
|
||||
```c
|
||||
#include <rtthread.h>
|
||||
#include <fdt.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
void *fdt = fdt_load_from_fs("sample.dtb");
|
||||
if (fdt != RT_NULL)
|
||||
{
|
||||
struct dtb_node *dtb_node_list = fdt_get_dtb_list(fdt);
|
||||
|
||||
if (dtb_node_list != RT_NULL)
|
||||
{
|
||||
/* load dtb node list OK */
|
||||
|
||||
/* free dtb_node_list here */
|
||||
fdt_free_dtb_list(dtb_node_list);
|
||||
}
|
||||
|
||||
rt_free(fdt);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## 打印设备树信息
|
||||
```c
|
||||
void fdt_get_dts_dump(struct dtb_node *dtb_node_head)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|dtb_node_head | 设备节点树头节点 |
|
||||
| **返回** | **描述** |
|
||||
|无返回值 | 无描述 |
|
||||
|
||||
## 遍历设备节点树并使用程序定义的回调函数
|
||||
```c
|
||||
void fdt_get_enum_dtb_node(struct dtb_node *dtb_node_head, void (*callback(struct dtb_node *dtb_node)))
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|dtb_node_head | 设备节点树头节点 |
|
||||
|callback | 程序定义的回调函数 |
|
||||
| **返回** | **描述** |
|
||||
|无返回值 | 无描述 |
|
||||
|
||||
示例:遍历设备树节点,并打印每个节点名称
|
||||
|
||||
```c
|
||||
#include <rtthread.h>
|
||||
#include <fdt.h>
|
||||
|
||||
void callback(struct dtb_node *node)
|
||||
{
|
||||
rt_kprintf("this node's name is %s\n", node->name);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
/* loaded dtb_node */
|
||||
extern struct dtb_node *dtb_node_list;
|
||||
|
||||
if (dtb_node_list != RT_NULL)
|
||||
{
|
||||
fdt_get_enum_dtb_node(dtb_node_list, callback);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## 通过节点名称查找节点
|
||||
```c
|
||||
struct dtb_node *fdt_get_dtb_node_by_name_DFS(struct dtb_node *dtb_node, const char *nodename)
|
||||
```
|
||||
```c
|
||||
struct dtb_node *fdt_get_dtb_node_by_name_BFS(struct dtb_node *dtb_node, const char *nodename)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|dtb_node | 设备节点树节点 |
|
||||
|nodename | 查找节点的名称 |
|
||||
| **返回** | **描述** |
|
||||
|struct dtb_node * | 返回查找的节点 |
|
||||
|RT_NULL | 未找到为RT_NULL |
|
||||
|
||||
## 通过节点路径查找节点
|
||||
```c
|
||||
struct dtb_node *fdt_get_dtb_node_by_path(struct dtb_node *dtb_node, const char *pathname)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|dtb_node | 设备节点树节点 |
|
||||
|pathname | 查找节点的路径 |
|
||||
| **返回** | **描述** |
|
||||
|struct dtb_node * | 返回查找的节点 |
|
||||
|RT_NULL | 未找到为RT_NULL |
|
||||
|
||||
## 通过节点phandle值查找节点
|
||||
```c
|
||||
struct dtb_node *fdt_get_dtb_node_by_phandle_DFS(struct dtb_node *dtb_node, phandle handle)
|
||||
```
|
||||
```c
|
||||
struct dtb_node *fdt_get_dtb_node_by_phandle_BFS(struct dtb_node *dtb_node, phandle handle)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|dtb_node | 设备节点树节点 |
|
||||
|handle | 查找节点的phandle值 |
|
||||
| **返回** | **描述** |
|
||||
|struct dtb_node * | 返回查找的节点 |
|
||||
|RT_NULL | 未找到为RT_NULL |
|
||||
|
||||
DFS和BFS是用于区分不同深度节点的搜索方法,时间复杂度和空间复杂度都较高,建议使用路径查找。
|
||||
|
||||
## 读取节点属性值的单位
|
||||
```c
|
||||
void fdt_get_dtb_node_cells(struct dtb_node *dtb_node, int *addr_cells, int *size_cells)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|dtb_node | 设备节点树节点 |
|
||||
|addr_cells | 返回的地址块的单位(u32)大小 |
|
||||
|size_cells | 返回的尺寸块的单位(u32)大小 |
|
||||
| **返回** | **描述** |
|
||||
|无返回值 | 无描述 |
|
||||
|
||||
|
||||
## 读取节点属性值
|
||||
```c
|
||||
void *fdt_get_dtb_node_property(struct dtb_node *dtb_node, const char *property_name, rt_size_t *property_size)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|dtb_node | 设备节点树节点 |
|
||||
|property_name | 属性名称 |
|
||||
|property_size | 属性大小 |
|
||||
| **返回** | **描述** |
|
||||
|void * | 无描述 |
|
||||
|RT_NULL | 该设备树没有该属性 |
|
||||
|
||||
读取的值为在设备树中存储的值,CPU小端模式下可能需要使用其他API进行转换才是正确的值
|
||||
|
||||
## 读取预留内存信息
|
||||
```c
|
||||
struct dtb_memreserve *fdt_get_dtb_memreserve(struct dtb_node *dtb_node, int *memreserve_size)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|dtb_node | 设备节点树节点 |
|
||||
|memreserve_size | 返回的内存信息数组长度 |
|
||||
| **返回** | **描述** |
|
||||
|struct dtb_memreserve *| 内存预留信息数组的内存地址 |
|
||||
|RT_NULL | 该设备树没有预留内存信息 |
|
||||
|
||||
## 读取节点的状态
|
||||
```c
|
||||
rt_bool_t fdt_get_dtb_node_status(struct dtb_node *dtb_node)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|dtb_node | 设备节点树节点 |
|
||||
| **返回** | **描述** |
|
||||
|RT_FALSE | 状态为禁用 |
|
||||
|RT_TRUE | 状态为使用 |
|
||||
|
||||
## 用于参数为字符串列表的函数
|
||||
```c
|
||||
fdt_string_list(string, ...)
|
||||
```
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|string | 字符串集合 |
|
||||
|
||||
## 读取节点compatible标志匹配
|
||||
```c
|
||||
rt_bool_t fdt_get_dtb_node_compatible_match(struct dtb_node *dtb_node, char **compatibles)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|dtb_node | 设备节点树节点 |
|
||||
|compatibles | 所有要匹配的字符串列表 |
|
||||
| **返回** | **描述** |
|
||||
|RT_FALSE | 匹配失败 |
|
||||
|RT_TRUE | 匹配成功 |
|
||||
|
||||
## 读取属性值中的字符串
|
||||
```c
|
||||
char *fdt_get_dtb_string_list_value(void *value, int size, int index)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|value | 节点属性的内存地址 |
|
||||
|size | 节点属性的尺寸 |
|
||||
|index | 要读取字符串的索引 |
|
||||
| **返回** | **描述** |
|
||||
|char * | 字符串的内存地址 |
|
||||
|RT_NULL | 该索引没有字符串 |
|
||||
|
||||
## 读取值为字符串属性下一个字符串
|
||||
```c
|
||||
char *fdt_get_dtb_string_list_value_next(void *value, void *end)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|value | 节点属性的内存地址 |
|
||||
|end | 节点属性的结束地址 |
|
||||
| **返回** | **描述** |
|
||||
|char * | 字符串的内存地址 |
|
||||
|RT_NULL | 没有下一个字符串 |
|
||||
|
||||
## 读取值为u32属性的值
|
||||
```c
|
||||
rt_uint32_t fdt_get_dtb_cell_value(void *value)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|value | 节点属性的内存地址 |
|
||||
| **返回** | **描述** |
|
||||
|rt_uint32_t | 该值小端的值 |
|
||||
|
||||
## 读取值为u8属性的值
|
||||
```c
|
||||
rt_uint8_t fdt_get_dtb_byte_value(void *value)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|value | 节点属性的内存地址 |
|
||||
| **返回** | **描述** |
|
||||
|rt_uint8_t | 该值小端的值 |
|
||||
|
||||
# fdt foreach API
|
||||
|
||||
## 遍历属性中字符串宏
|
||||
```c
|
||||
for_each_property_string(node_ptr, property_name, str, size)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|node_ptr | 设备节点树节点 |
|
||||
|property_name | 节点属性名称 |
|
||||
|str | 每次遍历到的字符串 |
|
||||
|size | 用于保存节点属性的尺寸 |
|
||||
|
||||
## 遍历属性中u32宏
|
||||
```c
|
||||
for_each_property_cell(node_ptr, property_name, value, list, size)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|node_ptr | 设备节点树节点 |
|
||||
|property_name | 节点属性名称 |
|
||||
|value | 每次遍历到的值 |
|
||||
|list | 用于保存节点属性的值内存地址 |
|
||||
|size | 用于保存节点属性的尺寸 |
|
||||
|
||||
## 遍历属性中u8宏
|
||||
```c
|
||||
for_each_property_byte(node_ptr, property_name, value, list, size)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|node_ptr | 设备节点树节点 |
|
||||
|property_name | 节点属性名称 |
|
||||
|value | 每次遍历到的值 |
|
||||
|list | 用于保存节点属性的值内存地址 |
|
||||
|size | 用于保存节点属性的尺寸 |
|
||||
|
||||
## 遍历子节点宏
|
||||
```c
|
||||
for_each_node_child(node_ptr)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|node_ptr | 设备节点树节点 |
|
||||
|
||||
## 遍历兄弟节点宏
|
||||
```c
|
||||
for_each_node_sibling(node_ptr)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
|:------------------|:------------------------------------|
|
||||
|node_ptr | 设备节点树节点 |
|
81
components/drivers/fdt/docs/examples.md
Normal file
81
components/drivers/fdt/docs/examples.md
Normal file
|
@ -0,0 +1,81 @@
|
|||
# fdt示例 #
|
||||
|
||||
在`examples`文件夹中存放`bcm2711-rpi-4-b.dtb`和`vexpress-v2p-ca9.dtb`可供测试,如果系统可以从bootloader或其他方式获取到bsp本身的dtb,也可以通过修改示例程序进行测试
|
||||
|
||||
## fdt_dump
|
||||
```bash
|
||||
fdt_dump vexpress-v2p-ca9.dtb
|
||||
```
|
||||
|
||||
#### 示例结果 ####
|
||||
```bash
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
model = "V2P-CA9";
|
||||
arm,hbi = <0x191>;
|
||||
arm,vexpress,site = <0xf>;
|
||||
compatible = "arm,vexpress,v2p-ca9", "arm,vexpress";
|
||||
interrupt-parent = <0x1>;
|
||||
#address-cells = <0x1>;
|
||||
#size-cells = <0x1>;
|
||||
|
||||
chosen {
|
||||
};
|
||||
|
||||
aliases {
|
||||
serial0 = "/smb@4000000/motherboard/iofpga@7,00000000/uart@9000";
|
||||
serial1 = "/smb@4000000/motherboard/iofpga@7,00000000/uart@a000";
|
||||
serial2 = "/smb@4000000/motherboard/iofpga@7,00000000/uart@b000";
|
||||
serial3 = "/smb@4000000/motherboard/iofpga@7,00000000/uart@c000";
|
||||
i2c0 = "/smb@4000000/motherboard/iofpga@7,00000000/i2c@16000";
|
||||
i2c1 = "/smb@4000000/motherboard/iofpga@7,00000000/i2c@2000";
|
||||
};
|
||||
|
||||
...... 省略
|
||||
|
||||
hsb@e0000000 {
|
||||
compatible = "simple-bus";
|
||||
#address-cells = <0x1>;
|
||||
#size-cells = <0x1>;
|
||||
ranges = <0x0 0xe0000000 0x20000000>;
|
||||
#interrupt-cells = <0x1>;
|
||||
interrupt-map-mask = <0x0 0x3>;
|
||||
interrupt-map = <0x0 0x0 0x1 0x0 0x24 0x4 0x0 0x1 0x1 0x0 0x25 0x4 0x0 0x2 0x1 0x0 0x26 0x4 0x0 0x3 0x1 0x0 0x27 0x4>;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## fdt_test
|
||||
```bash
|
||||
fdt_test
|
||||
```
|
||||
|
||||
#### 示例结果 ####
|
||||
```bash
|
||||
name = uart@9000
|
||||
reg = <0x9000,0x1000>;
|
||||
compatible = "arm,pl011","arm,primecell";
|
||||
|
||||
name = cpus
|
||||
path = /cpus/cpu@0/
|
||||
path = /cpus/cpu@1/
|
||||
path = /cpus/cpu@2/
|
||||
path = /cpus/cpu@3/
|
||||
|
||||
name = user1, lable = v2m:green:user1
|
||||
name = user2, lable = v2m:green:user2
|
||||
name = user3, lable = v2m:green:user3
|
||||
name = user4, lable = v2m:green:user4
|
||||
name = user5, lable = v2m:green:user5
|
||||
name = user6, lable = v2m:green:user6
|
||||
name = user7, lable = v2m:green:user7
|
||||
name = user8, lable = v2m:green:user8
|
||||
|
||||
/memreserve/ 0x0000000000000000 0x0000000000001000;
|
||||
|
||||
phandle = <0x9>
|
||||
name = bt_pins
|
||||
path = /soc/gpio@7e200000/bt_pins/
|
||||
brcm,pins = [2d 00]
|
||||
```
|
7
components/drivers/fdt/docs/version.md
Normal file
7
components/drivers/fdt/docs/version.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# 版本和修订 #
|
||||
|
||||
| Date | Version | Author | Note |
|
||||
| -------- | :-----: | :---- | :---- |
|
||||
| 2021-9-1 | v0.0.1 | GuEe-GUI | 初始版本 |
|
||||
| 2021-11-11 | v1.0.0 | GuEe-GUI | API确定版本 |
|
||||
| | | | |
|
10
components/drivers/fdt/examples/SConscript
Normal file
10
components/drivers/fdt/examples/SConscript
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('*.c')
|
||||
CPPPATH = [cwd]
|
||||
|
||||
group = DefineGroup('FDT', src, depend = ['FDT_USING_DEBUG'], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
143
components/drivers/fdt/examples/fdt_test.c
Normal file
143
components/drivers/fdt/examples/fdt_test.c
Normal file
|
@ -0,0 +1,143 @@
|
|||
#include <rtthread.h>
|
||||
#include "dtb_node.h"
|
||||
|
||||
int dtb_test()
|
||||
{
|
||||
void *fdt;
|
||||
|
||||
if ((fdt = dtb_node_load_from_fs("vexpress-v2p-ca9.dtb")) != RT_NULL)
|
||||
{
|
||||
struct dtb_node *dtb_node_list = dtb_node_get_dtb_list(fdt);
|
||||
if (dtb_node_list != RT_NULL)
|
||||
{
|
||||
struct dtb_node *serial0 = dtb_node_get_dtb_node_by_path(dtb_node_list, "/smb@4000000/motherboard/iofpga@7,00000000/uart@9000");
|
||||
struct dtb_node *cpu = dtb_node_get_dtb_node_by_path(dtb_node_list, "/cpus");
|
||||
struct dtb_node *user1 = dtb_node_get_dtb_node_by_path(dtb_node_list, "/smb@4000000/motherboard/leds/user1");
|
||||
|
||||
if (serial0 != RT_NULL)
|
||||
{
|
||||
int property_size;
|
||||
rt_uint32_t u32_value;
|
||||
rt_uint32_t *u32_ptr;
|
||||
char *str_ptr;
|
||||
|
||||
rt_kprintf("name = %s\n", serial0->name);
|
||||
|
||||
rt_kputs("reg = <");
|
||||
for_each_property_cell(serial0, "reg", u32_value, u32_ptr, property_size)
|
||||
{
|
||||
rt_kprintf("0x%x,", u32_value);
|
||||
}
|
||||
rt_kputs("\b>;\n");
|
||||
|
||||
rt_kputs("compatible = ");
|
||||
for_each_property_string(serial0, "compatible", str_ptr, property_size)
|
||||
{
|
||||
rt_kprintf("\"%s\",", str_ptr);
|
||||
}
|
||||
rt_kputs("\b;\n");
|
||||
}
|
||||
|
||||
if (cpu != RT_NULL)
|
||||
{
|
||||
struct dtb_node *cpu_child = cpu;
|
||||
|
||||
rt_kprintf("\nname = %s\n", cpu->name);
|
||||
|
||||
for_each_node_child(cpu_child)
|
||||
{
|
||||
rt_kprintf("path = %s\n", cpu_child->path);
|
||||
}
|
||||
}
|
||||
|
||||
if (user1 != RT_NULL)
|
||||
{
|
||||
struct dtb_node *user = user1;
|
||||
|
||||
rt_kprintf("\nname = %s, lable = %s\n", user1->name, dtb_node_get_dtb_node_property(user1, "label", RT_NULL));
|
||||
|
||||
for_each_node_sibling(user)
|
||||
{
|
||||
rt_kprintf("name = %s, lable = %s\n", user->name, dtb_node_get_dtb_node_property(user, "label", RT_NULL));
|
||||
}
|
||||
}
|
||||
}
|
||||
dtb_node_free_dtb_list(dtb_node_list);
|
||||
}
|
||||
|
||||
if ((fdt = dtb_node_load_from_fs("bcm2711-rpi-4-b.dtb")) != RT_NULL)
|
||||
{
|
||||
struct dtb_node *dtb_node_list = dtb_node_get_dtb_list(fdt);
|
||||
if (dtb_node_list != RT_NULL)
|
||||
{
|
||||
struct dtb_node *bt_pins;
|
||||
int memreserve_size;
|
||||
struct dtb_memreserve *memreserve;
|
||||
|
||||
bt_pins = dtb_node_get_dtb_node_by_name_DFS(dtb_node_list, "bt_pins");
|
||||
bt_pins = dtb_node_get_dtb_node_by_name_BFS(dtb_node_list, bt_pins->name);
|
||||
bt_pins = dtb_node_get_dtb_node_by_phandle_DFS(dtb_node_list, bt_pins->handle);
|
||||
bt_pins = dtb_node_get_dtb_node_by_phandle_BFS(dtb_node_list, bt_pins->handle);
|
||||
|
||||
memreserve = dtb_node_get_dtb_memreserve(bt_pins, &memreserve_size);
|
||||
if (memreserve_size > 0)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < memreserve_size; ++i)
|
||||
{
|
||||
rt_kputs("\n/memreserve/\t");
|
||||
if (IN_64BITS_MODE)
|
||||
{
|
||||
rt_kprintf("0x%016x 0x%016x;", memreserve[i].address, memreserve[i].size);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("0x%08x%08x 0x%08x%08x;", memreserve[i].address, memreserve[i].size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bt_pins != RT_NULL)
|
||||
{
|
||||
int property_size;
|
||||
rt_uint8_t u8_value;
|
||||
rt_uint8_t *u8_ptr;
|
||||
|
||||
rt_kprintf("\n\nphandle = <0x%x>\n", bt_pins->handle);
|
||||
rt_kprintf("name = %s\n", bt_pins->name);
|
||||
rt_kprintf("path = %s\n", bt_pins->path);
|
||||
|
||||
rt_kputs("brcm,pins = [");
|
||||
for_each_property_byte(bt_pins, "brcm,pins", u8_value, u8_ptr, property_size)
|
||||
{
|
||||
rt_kprintf("%02x ", u8_value);
|
||||
}
|
||||
rt_kputs("\b]\n");
|
||||
}
|
||||
}
|
||||
dtb_node_free_dtb_list(dtb_node_list);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT(dtb_test, dtb API test);
|
||||
|
||||
int dtb_dump(int argc, char** argv)
|
||||
{
|
||||
struct dtb_node *dtb_root_node = get_dtb_node_head();
|
||||
if(argc == 1)
|
||||
{
|
||||
if (dtb_root_node != RT_NULL)
|
||||
{
|
||||
dtb_node_get_dts_dump(dtb_root_node);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("invailed parameter\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT(dtb_dump, dtb dump from mem);
|
||||
|
||||
|
381
components/drivers/fdt/inc/dtb_node.h
Normal file
381
components/drivers/fdt/inc/dtb_node.h
Normal file
|
@ -0,0 +1,381 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _DTB_NODE_H__
|
||||
#define _DTB_NODE_H__
|
||||
|
||||
#include "libfdt_env.h"
|
||||
#include <rtthread.h>
|
||||
#include <stdint.h>
|
||||
|
||||
//#define RT_DTB_DEBUG
|
||||
#ifdef RT_DTB_DEBUG
|
||||
#define debug(fmt, args...) rt_kprintf(fmt, ##args)
|
||||
#else
|
||||
#define debug(fmt, args...)
|
||||
#endif
|
||||
|
||||
#define ERR_PTR(err) ((void *)((long)(err)))
|
||||
#define PTR_ERR(ptr) ((long)(ptr))
|
||||
#define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)(-1000))
|
||||
|
||||
#define DEV_ROOT_NODE_ADDR_CELLS_DEFAULT 2
|
||||
#define DEV_ROOT_NODE_SIZE_CELLS_DEFAULT 1
|
||||
/* will be optimized to only u64 or u32 by gcc */
|
||||
#define IN_64BITS_MODE (sizeof(void *) == 8)
|
||||
|
||||
#define FDT_ROOT_ADDR_CELLS_DEFAULT 1
|
||||
#define FDT_ROOT_SIZE_CELLS_DEFAULT 1
|
||||
|
||||
#define FDT_DTB_ALL_NODES_PATH_SIZE (32 * 1024)
|
||||
#define FDT_DTB_PAD_SIZE 1024
|
||||
|
||||
#define FDT_RET_NO_MEMORY 2
|
||||
#define FDT_RET_NO_LOADED 1
|
||||
#define FDT_RET_GET_OK 0
|
||||
#define FDT_RET_GET_EMPTY (-1)
|
||||
|
||||
typedef uint32_t phandle;
|
||||
|
||||
struct dtb_memreserve
|
||||
{
|
||||
uintptr_t address;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct dtb_header
|
||||
{
|
||||
char root, zero; /* "/" */
|
||||
struct dtb_memreserve *memreserve;
|
||||
size_t memreserve_sz;
|
||||
};
|
||||
|
||||
struct dtb_property
|
||||
{
|
||||
const char *name;
|
||||
int size;
|
||||
void *value;
|
||||
|
||||
struct dtb_property *next;
|
||||
};
|
||||
|
||||
struct dtb_node
|
||||
{
|
||||
union
|
||||
{
|
||||
const char *name;
|
||||
const struct dtb_header *header;
|
||||
};
|
||||
const char *path;
|
||||
phandle handle;
|
||||
|
||||
struct dtb_property *properties;
|
||||
struct dtb_node *parent;
|
||||
struct dtb_node *child;
|
||||
struct dtb_node *sibling;
|
||||
};
|
||||
|
||||
#define FDT_MAX_PHANDLE_ARGS 16
|
||||
|
||||
/**
|
||||
* struct dtb_node_phandle_args - structure to hold phandle and arguments
|
||||
*
|
||||
* This is used when decoding a phandle in a device tree property. Typically
|
||||
* these look like this:
|
||||
*
|
||||
* wibble {
|
||||
* phandle = <5>;
|
||||
* };
|
||||
*
|
||||
* ...
|
||||
* some-prop = <&wibble 1 2 3>
|
||||
*
|
||||
* Here &node is the phandle of the node 'wibble', i.e. 5. There are three
|
||||
* arguments: 1, 2, 3.
|
||||
*
|
||||
* So when decoding the phandle in some-prop, np will point to wibble,
|
||||
* args_count will be 3 and the three arguments will be in args.
|
||||
*
|
||||
* @np: Node that the phandle refers to
|
||||
* @args_count: Number of arguments
|
||||
* @args: Argument values
|
||||
*/
|
||||
struct fdt_phandle_args
|
||||
{
|
||||
struct dtb_node *np;
|
||||
int args_count;
|
||||
uint32_t args[FDT_MAX_PHANDLE_ARGS];
|
||||
};
|
||||
|
||||
/*
|
||||
* A single signal can be specified via a range of minimal and maximal values
|
||||
* with a typical value, that lies somewhere inbetween.
|
||||
*/
|
||||
struct timing_entry
|
||||
{
|
||||
uint32_t min;
|
||||
uint32_t typ;
|
||||
uint32_t max;
|
||||
};
|
||||
|
||||
void *get_fdt_blob(void);
|
||||
struct dtb_node *get_dtb_node_head(void);
|
||||
rt_bool_t dtb_node_active(void);
|
||||
int device_tree_setup(void *mem_addr);
|
||||
|
||||
void *dtb_node_load_from_fs(char *dtb_filename);
|
||||
void *dtb_node_load_from_memory(void *dtb_ptr, rt_bool_t is_clone);
|
||||
|
||||
size_t dtb_node_set_linux_cmdline(void *fdt, char *cmdline);
|
||||
size_t dtb_node_set_linux_initrd(void *fdt, uint64_t initrd_addr, size_t initrd_size);
|
||||
|
||||
size_t dtb_node_set_dtb_property(void *fdt, char *pathname, char *property_name, uint32_t *cells, size_t cells_size);
|
||||
size_t dtb_node_add_dtb_memreserve(void *fdt, uint64_t address, uint64_t size);
|
||||
size_t dtb_node_del_dtb_memreserve(void *fdt, uint64_t address);
|
||||
|
||||
int dtb_node_get_exec_status();
|
||||
struct dtb_node *dtb_node_get_dtb_list(void *fdt);
|
||||
void dtb_node_free_dtb_list(struct dtb_node *dtb_node_head);
|
||||
void dtb_node_get_dts_dump(struct dtb_node *dtb_node_head);
|
||||
void dtb_node_get_enum_dtb_node(struct dtb_node *dtb_node_head, void(callback(struct dtb_node *dtb_node)));
|
||||
|
||||
struct dtb_node *dtb_node_get_dtb_node_by_name_DFS(struct dtb_node *dtb_node, const char *nodename);
|
||||
struct dtb_node *dtb_node_get_dtb_node_by_name_BFS(struct dtb_node *dtb_node, const char *nodename);
|
||||
struct dtb_node *dtb_node_get_dtb_node_by_path(struct dtb_node *dtb_node, const char *pathname);
|
||||
struct dtb_node *dtb_node_get_dtb_node_by_phandle_DFS(struct dtb_node *dtb_node, phandle handle);
|
||||
struct dtb_node *dtb_node_get_dtb_node_by_phandle_BFS(struct dtb_node *dtb_node, phandle handle);
|
||||
void dtb_node_get_dtb_node_cells(struct dtb_node *dtb_node, int *addr_cells, int *size_cells);
|
||||
struct dtb_memreserve *dtb_node_get_dtb_memreserve(struct dtb_node *dtb_node, int *memreserve_size);
|
||||
uint8_t dtb_node_get_dtb_byte_value(void *value);
|
||||
|
||||
char *dtb_node_get_dtb_string_list_value(void *value, int size, int index);
|
||||
char *dtb_node_get_dtb_string_list_value_next(void *value, void *end);
|
||||
uint32_t dtb_node_get_dtb_cell_value(void *value);
|
||||
|
||||
rt_bool_t dtb_node_get_dtb_node_status(const struct dtb_node *dtb_node);
|
||||
rt_bool_t dtb_node_get_dtb_node_compatible_match(const struct dtb_node *dtb_node, const char *compatibles);
|
||||
|
||||
/*dtb_node_access.c */
|
||||
int dtb_node_read_u32(const struct dtb_node *dn, const char *propname, uint32_t *outp);
|
||||
uint32_t dtb_node_read_u32_default(const struct dtb_node *node, const char *propname, uint32_t def);
|
||||
int dtb_node_read_u32_index(const struct dtb_node *node, const char *propname, int index,
|
||||
uint32_t *outp);
|
||||
uint32_t dtb_node_read_u32_index_default(const struct dtb_node *node, const char *propname, int index,
|
||||
uint32_t def);
|
||||
int dtb_node_read_u32_array(const struct dtb_node *dn, const char *propname,
|
||||
uint32_t *out_values, size_t sz);
|
||||
int dtb_node_read_u32_index(const struct dtb_node *dn, const char *propname,
|
||||
int index, uint32_t *outp);
|
||||
int dtb_node_read_s32_default(const struct dtb_node *node, const char *propname, int32_t def);
|
||||
int dtb_node_read_u64(const struct dtb_node *dn, const char *propname, uint64_t *outp);
|
||||
uint64_t dtb_node_read_u64_default(const struct dtb_node *node, const char *propname, uint64_t def);
|
||||
|
||||
int dtb_node_n_addr_cells(const struct dtb_node *dn);
|
||||
int dtb_node_n_size_cells(const struct dtb_node *dn);
|
||||
int dtb_node_simple_addr_cells(const struct dtb_node *np);
|
||||
int dtb_node_simple_size_cells(const struct dtb_node *np);
|
||||
|
||||
struct dtb_node *dtb_node_find_all_nodes(const struct dtb_node *prev);
|
||||
struct dtb_node *dtb_node_find_node_by_phandle(phandle handle);
|
||||
struct dtb_node *dtb_node_find_compatible_node(struct dtb_node *from, const char *compatible);
|
||||
void *dtb_node_get_dtb_node_property_value(const struct dtb_node *dtb_node, const char *property_name, int *property_size);
|
||||
struct dtb_property *dtb_node_get_dtb_node_property(const struct dtb_node *dtb_node, const char *property_name, int *property_size);
|
||||
const struct dtb_node *dtb_node_find_node_by_prop_value(struct dtb_node *from, const char *propname, const void *propval, int proplen);
|
||||
struct dtb_node *dtb_node_find_node_opts_by_path(const char *path,
|
||||
const char **opts);
|
||||
|
||||
static inline struct dtb_node *dtb_node_find_node_by_path(const char *path)
|
||||
{
|
||||
return dtb_node_find_node_opts_by_path(path, NULL);
|
||||
}
|
||||
|
||||
rt_bool_t dtb_node_device_is_available(const struct dtb_node *device);
|
||||
struct dtb_node *dtb_node_get_parent(const struct dtb_node *node);
|
||||
int dtb_node_property_match_string(const struct dtb_node *dn, const char *propname,
|
||||
const char *string);
|
||||
int dtb_node_property_read_string_helper(const struct dtb_node *dn,
|
||||
const char *propname, const char **out_strs,
|
||||
size_t sz, int skip);
|
||||
/**
|
||||
* of_property_read_string_index() - Find and read a string from a multiple
|
||||
* strings property.
|
||||
* @np: device node from which the property value is to be read.
|
||||
* @propname: name of the property to be searched.
|
||||
* @index: index of the string in the list of strings
|
||||
* @out_string: pointer to null terminated return string, modified only if
|
||||
* return value is 0.
|
||||
*
|
||||
* Search for a property in a device tree node and retrieve a null
|
||||
* terminated string value (pointer to data, not a copy) in the list of strings
|
||||
* contained in that property.
|
||||
* Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if
|
||||
* property does not have a value, and -EILSEQ if the string is not
|
||||
* null-terminated within the length of the property data.
|
||||
*
|
||||
* The out_string pointer is modified only if a valid string can be decoded.
|
||||
*/
|
||||
static inline int dtb_node_property_read_string_index(const struct dtb_node *dn,
|
||||
const char *propname,
|
||||
int index, const char **output)
|
||||
{
|
||||
int rc = dtb_node_property_read_string_helper(dn, propname, output, 1, index);
|
||||
return rc < 0 ? rc : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_property_count_strings() - Find and return the number of strings from a
|
||||
* multiple strings property.
|
||||
* @np: device node from which the property value is to be read.
|
||||
* @propname: name of the property to be searched.
|
||||
*
|
||||
* Search for a property in a device tree node and retrieve the number of null
|
||||
* terminated string contain in it. Returns the number of strings on
|
||||
* success, -EINVAL if the property does not exist, -ENODATA if property
|
||||
* does not have a value, and -EILSEQ if the string is not null-terminated
|
||||
* within the length of the property data.
|
||||
*/
|
||||
static inline int dtb_node_property_count_strings(const struct dtb_node *dn,
|
||||
const char *propname)
|
||||
{
|
||||
return dtb_node_property_read_string_helper(dn, propname, NULL, 0, 0);
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_parse_phandle(const struct dtb_node *dn,
|
||||
const char *phandle_name, int index);
|
||||
int dtb_node_parse_phandle_with_args(const struct dtb_node *dn,
|
||||
const char *list_name, const char *cells_name,
|
||||
int index, struct fdt_phandle_args *out_args);
|
||||
int dtb_node_count_phandle_with_args(const struct dtb_node *dn,
|
||||
const char *list_name, const char *cells_name);
|
||||
|
||||
/* dtb_node_addr.c */
|
||||
const uint32_t *dtb_node_get_address(const struct dtb_node *dev, int index,
|
||||
uint64_t *size, unsigned int *flags);
|
||||
|
||||
#define dtb_node_string_list(string, ...) ((char *[]){string, ##__VA_ARGS__, NULL})
|
||||
|
||||
#define for_each_property_string(node_ptr, property_name, str, size) \
|
||||
for (str = dtb_node_get_dtb_node_property_value(node_ptr, property_name, &size), \
|
||||
size += (typeof(size))(size_t)str; \
|
||||
str && *str; \
|
||||
str = dtb_node_get_dtb_string_list_value_next((void *)str, (void *)(size_t)size))
|
||||
|
||||
#define for_each_property_cell(node_ptr, property_name, value, list, size) \
|
||||
for (list = dtb_node_get_dtb_node_property_value(node_ptr, property_name, &size), \
|
||||
value = dtb_node_get_dtb_cell_value(list), \
|
||||
size /= sizeof(uint32_t); \
|
||||
size > 0; \
|
||||
value = dtb_node_get_dtb_cell_value(++list), --size)
|
||||
|
||||
#define for_each_property_byte(node_ptr, property_name, value, list, size) \
|
||||
for (list = dtb_node_get_dtb_node_property_value(node_ptr, property_name, &size), \
|
||||
value = dtb_node_get_dtb_byte_value(list); \
|
||||
size > 0; \
|
||||
value = dtb_node_get_dtb_byte_value(++list), --size)
|
||||
|
||||
#define for_each_node_child(node_ptr) \
|
||||
for (node_ptr = (node_ptr ? node_ptr->child : NULL); \
|
||||
node_ptr != NULL; \
|
||||
node_ptr = node_ptr->sibling)
|
||||
|
||||
#define for_each_node_sibling(node_ptr) \
|
||||
for (node_ptr = (node_ptr ? node_ptr->sibling : NULL); \
|
||||
node_ptr != NULL; \
|
||||
node_ptr = node_ptr->sibling)
|
||||
|
||||
#define for_each_of_allnodes_from(from, dn) \
|
||||
for (dn = dtb_node_find_all_nodes(from); dn; dn = dtb_node_find_all_nodes(dn))
|
||||
|
||||
#define for_each_of_allnodes(dn) for_each_of_allnodes_from(NULL, dn)
|
||||
|
||||
#define dtb_node_get(x) (x)
|
||||
static inline void dtb_node_put(const struct dtb_node *np)
|
||||
{
|
||||
}
|
||||
|
||||
/* Helper to read a big number; size is in cells (not bytes) */
|
||||
static inline uint64_t dtb_node_read_number(const uint32_t *cell, int size)
|
||||
{
|
||||
uint64_t r = 0;
|
||||
while (size--)
|
||||
r = (r << 32) | fdt32_to_cpu(*(cell++));
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* ofnode_valid() - check if an ofnode is valid
|
||||
*
|
||||
* @return true if the reference contains a valid ofnode, RT_FALSE if it is NULL
|
||||
*/
|
||||
static inline rt_bool_t dtb_node_valid(const struct dtb_node *node)
|
||||
{
|
||||
if (dtb_node_active())
|
||||
return node != NULL;
|
||||
return RT_FALSE;
|
||||
}
|
||||
|
||||
/*dtb_base.c */
|
||||
rt_bool_t dtb_node_read_bool(const struct dtb_node *node, const char *propname);
|
||||
const void *dtb_node_read_prop(const struct dtb_node *node, const char *propname, int *sizep);
|
||||
const char *dtb_node_read_string(const struct dtb_node *node, const char *propname);
|
||||
const struct dtb_node *dtb_node_find_subnode(const struct dtb_node *node, const char *subnode_name);
|
||||
int dtb_node_read_u32_array(const struct dtb_node *node, const char *propname,
|
||||
uint32_t *out_values, size_t sz);
|
||||
struct dtb_node *dtb_node_first_subnode(const struct dtb_node *node);
|
||||
struct dtb_node *dtb_node_next_subnode(const struct dtb_node *node);
|
||||
struct dtb_node *dtb_node_get_parent(const struct dtb_node *node);
|
||||
const char *dtb_node_get_name(const struct dtb_node *node);
|
||||
struct dtb_node *dtb_node_get_by_phandle(uint32_t phandle);
|
||||
int dtb_node_read_size(const struct dtb_node *node, const char *propname);
|
||||
int dtb_node_get_addr_and_size_by_index(const struct dtb_node *node, int index, size_t *addr, size_t *size);
|
||||
size_t dtb_node_get_addr_index(const struct dtb_node *node, int index);
|
||||
size_t dtb_node_get_addr(const struct dtb_node *node);
|
||||
int dtb_node_stringlist_search(const struct dtb_node *node, const char *property,
|
||||
const char *string);
|
||||
int dtb_node_read_string_index(const struct dtb_node *node, const char *property, int index,
|
||||
const char **outp);
|
||||
int dtb_node_read_string_count(const struct dtb_node *node, const char *property);
|
||||
struct dtb_node *dtb_node_path(const char *path);
|
||||
const char *dtb_node_get_chosen_prop(const char *name);
|
||||
struct dtb_node *dtb_node_get_chosen_node(const char *name);
|
||||
const void *dtb_node_get_property(const struct dtb_node *node, const char *propname, int *lenp);
|
||||
rt_bool_t dtb_node_is_available(const struct dtb_node *node);
|
||||
size_t dtb_node_get_addr_size(const struct dtb_node *node, const char *property,
|
||||
size_t *sizep);
|
||||
const uint8_t *dtb_node_read_u8_array_ptr(const struct dtb_node *node, const char *propname, size_t sz);
|
||||
int dtb_node_find_all_compatible_node(const struct dtb_node *from, const char *compatible, struct dtb_node **node_table, int max_num, int *node_num);
|
||||
int dtb_node_write_prop(const struct dtb_node *node, const char *propname, int len,
|
||||
const void *value);
|
||||
int dtb_node_write_string(const struct dtb_node *node, const char *propname, const char *value);
|
||||
int dtb_node_set_enabled(const struct dtb_node *node, rt_bool_t value);
|
||||
int dtb_node_irq_get(struct dtb_node *dev, int index);
|
||||
int dtb_node_irq_get_byname(struct dtb_node *dev, const char *name);
|
||||
int dtb_node_irq_count(struct dtb_node *dev);
|
||||
|
||||
/**
|
||||
* dtb_node_for_each_subnode() - iterate over all subnodes of a parent
|
||||
*
|
||||
* @node: child node (ofnode, lvalue)
|
||||
* @parent: parent node (ofnode)
|
||||
*
|
||||
* This is a wrapper around a for loop and is used like so:
|
||||
*
|
||||
* ofnode node;
|
||||
*
|
||||
* dtb_node_for_each_subnode(node, parent) {
|
||||
* Use node
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* Note that this is implemented as a macro and @node is used as
|
||||
* iterator in the loop. The parent variable can be a constant or even a
|
||||
* literal.
|
||||
*/
|
||||
#define dtb_node_for_each_subnode(node, parent) \
|
||||
for (node = dtb_node_first_subnode(parent); \
|
||||
dtb_node_valid(node); \
|
||||
node = dtb_node_next_subnode(node))
|
||||
|
||||
#endif /* RT_FDT_H__ */
|
10
components/drivers/fdt/libfdt/SConscript
Normal file
10
components/drivers/fdt/libfdt/SConscript
Normal file
|
@ -0,0 +1,10 @@
|
|||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('*.c')
|
||||
|
||||
CPPPATH = [cwd]
|
||||
|
||||
group = DefineGroup('FDT', src, depend = ['RT_USING_FDTLIB'], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
249
components/drivers/fdt/libfdt/fdt.c
Normal file
249
components/drivers/fdt/libfdt/fdt.c
Normal file
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
#include "fdt.h"
|
||||
#include "libfdt.h"
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
int fdt_check_header(const void *fdt)
|
||||
{
|
||||
if (fdt_magic(fdt) == FDT_MAGIC) {
|
||||
/* Complete tree */
|
||||
if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
|
||||
return -FDT_ERR_BADVERSION;
|
||||
if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
|
||||
return -FDT_ERR_BADVERSION;
|
||||
} else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
|
||||
/* Unfinished sequential-write blob */
|
||||
if (fdt_size_dt_struct(fdt) == 0)
|
||||
return -FDT_ERR_BADSTATE;
|
||||
} else {
|
||||
return -FDT_ERR_BADMAGIC;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
|
||||
{
|
||||
unsigned absoffset = offset + fdt_off_dt_struct(fdt);
|
||||
|
||||
if ((absoffset < offset)
|
||||
|| ((absoffset + len) < absoffset)
|
||||
|| (absoffset + len) > fdt_totalsize(fdt))
|
||||
return NULL;
|
||||
|
||||
if (fdt_version(fdt) >= 0x11)
|
||||
if (((offset + len) < offset)
|
||||
|| ((offset + len) > fdt_size_dt_struct(fdt)))
|
||||
return NULL;
|
||||
|
||||
return _fdt_offset_ptr(fdt, offset);
|
||||
}
|
||||
|
||||
uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
|
||||
{
|
||||
const fdt32_t *tagp, *lenp;
|
||||
uint32_t tag;
|
||||
int offset = startoffset;
|
||||
const char *p;
|
||||
|
||||
*nextoffset = -FDT_ERR_TRUNCATED;
|
||||
tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
|
||||
if (!tagp)
|
||||
return FDT_END; /* premature end */
|
||||
tag = fdt32_to_cpu(*tagp);
|
||||
offset += FDT_TAGSIZE;
|
||||
|
||||
*nextoffset = -FDT_ERR_BADSTRUCTURE;
|
||||
switch (tag) {
|
||||
case FDT_BEGIN_NODE:
|
||||
/* skip name */
|
||||
do {
|
||||
p = fdt_offset_ptr(fdt, offset++, 1);
|
||||
} while (p && (*p != '\0'));
|
||||
if (!p)
|
||||
return FDT_END; /* premature end */
|
||||
break;
|
||||
|
||||
case FDT_PROP:
|
||||
lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
|
||||
if (!lenp)
|
||||
return FDT_END; /* premature end */
|
||||
/* skip-name offset, length and value */
|
||||
offset += sizeof(struct fdt_property) - FDT_TAGSIZE
|
||||
+ fdt32_to_cpu(*lenp);
|
||||
break;
|
||||
|
||||
case FDT_END:
|
||||
case FDT_END_NODE:
|
||||
case FDT_NOP:
|
||||
break;
|
||||
|
||||
default:
|
||||
return FDT_END;
|
||||
}
|
||||
|
||||
if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
|
||||
return FDT_END; /* premature end */
|
||||
|
||||
*nextoffset = FDT_TAGALIGN(offset);
|
||||
return tag;
|
||||
}
|
||||
|
||||
int _fdt_check_node_offset(const void *fdt, int offset)
|
||||
{
|
||||
if ((offset < 0) || (offset % FDT_TAGSIZE)
|
||||
|| (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
int _fdt_check_prop_offset(const void *fdt, int offset)
|
||||
{
|
||||
if ((offset < 0) || (offset % FDT_TAGSIZE)
|
||||
|| (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
int fdt_next_node(const void *fdt, int offset, int *depth)
|
||||
{
|
||||
int nextoffset = 0;
|
||||
uint32_t tag;
|
||||
|
||||
if (offset >= 0)
|
||||
if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0)
|
||||
return nextoffset;
|
||||
|
||||
do {
|
||||
offset = nextoffset;
|
||||
tag = fdt_next_tag(fdt, offset, &nextoffset);
|
||||
|
||||
switch (tag) {
|
||||
case FDT_PROP:
|
||||
case FDT_NOP:
|
||||
break;
|
||||
|
||||
case FDT_BEGIN_NODE:
|
||||
if (depth)
|
||||
(*depth)++;
|
||||
break;
|
||||
|
||||
case FDT_END_NODE:
|
||||
if (depth && ((--(*depth)) < 0))
|
||||
return nextoffset;
|
||||
break;
|
||||
|
||||
case FDT_END:
|
||||
if ((nextoffset >= 0)
|
||||
|| ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
else
|
||||
return nextoffset;
|
||||
}
|
||||
} while (tag != FDT_BEGIN_NODE);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
int fdt_first_subnode(const void *fdt, int offset)
|
||||
{
|
||||
int depth = 0;
|
||||
|
||||
offset = fdt_next_node(fdt, offset, &depth);
|
||||
if (offset < 0 || depth != 1)
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
int fdt_next_subnode(const void *fdt, int offset)
|
||||
{
|
||||
int depth = 1;
|
||||
|
||||
/*
|
||||
* With respect to the parent, the depth of the next subnode will be
|
||||
* the same as the last.
|
||||
*/
|
||||
do {
|
||||
offset = fdt_next_node(fdt, offset, &depth);
|
||||
if (offset < 0 || depth < 1)
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
} while (depth > 1);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
const char *_fdt_find_string(const char *strtab, int tabsize, const char *s)
|
||||
{
|
||||
int len = strlen(s) + 1;
|
||||
const char *last = strtab + tabsize - len;
|
||||
const char *p;
|
||||
|
||||
for (p = strtab; p <= last; p++)
|
||||
if (memcmp(p, s, len) == 0)
|
||||
return p;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fdt_move(const void *fdt, void *buf, int bufsize)
|
||||
{
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
if (fdt_totalsize(fdt) > bufsize)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
memmove(buf, fdt, fdt_totalsize(fdt));
|
||||
return 0;
|
||||
}
|
125
components/drivers/fdt/libfdt/fdt.h
Normal file
125
components/drivers/fdt/libfdt/fdt.h
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-12-20 RT-Thread the first version
|
||||
*/
|
||||
|
||||
#ifndef _FDT_H
|
||||
#define _FDT_H
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
* Copyright 2012 Kim Phillips, Freescale Semiconductor.
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
struct fdt_header {
|
||||
fdt32_t magic; /* magic word FDT_MAGIC */
|
||||
fdt32_t totalsize; /* total size of DT block */
|
||||
fdt32_t off_dt_struct; /* offset to structure */
|
||||
fdt32_t off_dt_strings; /* offset to strings */
|
||||
fdt32_t off_mem_rsvmap; /* offset to memory reserve map */
|
||||
fdt32_t version; /* format version */
|
||||
fdt32_t last_comp_version; /* last compatible version */
|
||||
|
||||
/* version 2 fields below */
|
||||
fdt32_t boot_cpuid_phys; /* Which physical CPU id we're
|
||||
booting on */
|
||||
/* version 3 fields below */
|
||||
fdt32_t size_dt_strings; /* size of the strings block */
|
||||
|
||||
/* version 17 fields below */
|
||||
fdt32_t size_dt_struct; /* size of the structure block */
|
||||
};
|
||||
|
||||
struct fdt_reserve_entry {
|
||||
fdt64_t address;
|
||||
fdt64_t size;
|
||||
};
|
||||
|
||||
struct fdt_node_header {
|
||||
fdt32_t tag;
|
||||
char name[0];
|
||||
};
|
||||
|
||||
struct fdt_property {
|
||||
fdt32_t tag;
|
||||
fdt32_t len;
|
||||
fdt32_t nameoff;
|
||||
char data[0];
|
||||
};
|
||||
|
||||
#endif /* !__ASSEMBLY */
|
||||
|
||||
#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */
|
||||
#define FDT_TAGSIZE sizeof(fdt32_t)
|
||||
|
||||
#define FDT_BEGIN_NODE 0x1 /* Start node: full name */
|
||||
#define FDT_END_NODE 0x2 /* End node */
|
||||
#define FDT_PROP 0x3 /* Property: name off,
|
||||
size, content */
|
||||
#define FDT_NOP 0x4 /* nop */
|
||||
#define FDT_END 0x9
|
||||
|
||||
#define FDT_V1_SIZE (7*sizeof(fdt32_t))
|
||||
#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t))
|
||||
#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t))
|
||||
#define FDT_V16_SIZE FDT_V3_SIZE
|
||||
#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t))
|
||||
|
||||
#endif /* _FDT_H */
|
99
components/drivers/fdt/libfdt/fdt_addresses.c
Normal file
99
components/drivers/fdt/libfdt/fdt_addresses.c
Normal file
|
@ -0,0 +1,99 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2014 David Gibson <david@gibson.dropbear.id.au>
|
||||
* Copyright (C) 2018 embedded brains GmbH
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
#include <fdt.h>
|
||||
#include <libfdt.h>
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
static int fdt_cells(const void *fdt, int nodeoffset, const char *name)
|
||||
{
|
||||
const fdt32_t *c;
|
||||
uint32_t val;
|
||||
int len;
|
||||
|
||||
c = fdt_getprop(fdt, nodeoffset, name, &len);
|
||||
if (!c)
|
||||
return len;
|
||||
|
||||
if (len != sizeof(*c))
|
||||
return -FDT_ERR_BADNCELLS;
|
||||
|
||||
val = fdt32_to_cpu(*c);
|
||||
if (val > FDT_MAX_NCELLS)
|
||||
return -FDT_ERR_BADNCELLS;
|
||||
|
||||
return (int)val;
|
||||
}
|
||||
|
||||
int fdt_address_cells(const void *fdt, int nodeoffset)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = fdt_cells(fdt, nodeoffset, "#address-cells");
|
||||
if (val == 0)
|
||||
return -FDT_ERR_BADNCELLS;
|
||||
if (val == -FDT_ERR_NOTFOUND)
|
||||
return 2;
|
||||
return val;
|
||||
}
|
||||
|
||||
int fdt_size_cells(const void *fdt, int nodeoffset)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = fdt_cells(fdt, nodeoffset, "#size-cells");
|
||||
if (val == -FDT_ERR_NOTFOUND)
|
||||
return 1;
|
||||
return val;
|
||||
}
|
||||
|
||||
/* This function assumes that [address|size]_cells is 1 or 2 */
|
||||
int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset,
|
||||
const char *name, uint64_t addr, uint64_t size)
|
||||
{
|
||||
int addr_cells, size_cells, ret;
|
||||
uint8_t data[sizeof(fdt64_t) * 2], *prop;
|
||||
|
||||
ret = fdt_address_cells(fdt, parent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
addr_cells = ret;
|
||||
|
||||
ret = fdt_size_cells(fdt, parent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
size_cells = ret;
|
||||
|
||||
/* check validity of address */
|
||||
prop = data;
|
||||
if (addr_cells == 1) {
|
||||
if ((addr > UINT32_MAX) || ((UINT32_MAX + 1 - addr) < size))
|
||||
return -FDT_ERR_BADVALUE;
|
||||
|
||||
fdt32_st(prop, (uint32_t)addr);
|
||||
} else if (addr_cells == 2) {
|
||||
fdt64_st(prop, addr);
|
||||
} else {
|
||||
return -FDT_ERR_BADNCELLS;
|
||||
}
|
||||
|
||||
/* check validity of size */
|
||||
prop += addr_cells * sizeof(fdt32_t);
|
||||
if (size_cells == 1) {
|
||||
if (size > UINT32_MAX)
|
||||
return -FDT_ERR_BADVALUE;
|
||||
|
||||
fdt32_st(prop, (uint32_t)size);
|
||||
} else if (size_cells == 2) {
|
||||
fdt64_st(prop, size);
|
||||
} else {
|
||||
return -FDT_ERR_BADNCELLS;
|
||||
}
|
||||
|
||||
return fdt_appendprop(fdt, nodeoffset, name, data,
|
||||
(addr_cells + size_cells) * sizeof(fdt32_t));
|
||||
}
|
84
components/drivers/fdt/libfdt/fdt_empty_tree.c
Normal file
84
components/drivers/fdt/libfdt/fdt_empty_tree.c
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2012 David Gibson, IBM Corporation.
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
|
||||
#include "fdt.h"
|
||||
#include "libfdt.h"
|
||||
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
int fdt_create_empty_tree(void *buf, int bufsize)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = fdt_create(buf, bufsize);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = fdt_finish_reservemap(buf);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = fdt_begin_node(buf, "");
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = fdt_end_node(buf);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = fdt_finish(buf);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return fdt_open_into(buf, buf, bufsize);
|
||||
}
|
||||
|
701
components/drivers/fdt/libfdt/fdt_ro.c
Normal file
701
components/drivers/fdt/libfdt/fdt_ro.c
Normal file
|
@ -0,0 +1,701 @@
|
|||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
#include "fdt.h"
|
||||
#include "libfdt.h"
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
static int _fdt_nodename_eq(const void *fdt, int offset,
|
||||
const char *s, int len)
|
||||
{
|
||||
const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
|
||||
|
||||
if (! p)
|
||||
/* short match */
|
||||
return 0;
|
||||
|
||||
if (memcmp(p, s, len) != 0)
|
||||
return 0;
|
||||
|
||||
if (p[len] == '\0')
|
||||
return 1;
|
||||
else if (!memchr(s, '@', len) && (p[len] == '@'))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *fdt_string(const void *fdt, int stroffset)
|
||||
{
|
||||
return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
|
||||
}
|
||||
|
||||
static int _fdt_string_eq(const void *fdt, int stroffset,
|
||||
const char *s, int len)
|
||||
{
|
||||
const char *p = fdt_string(fdt, stroffset);
|
||||
|
||||
return (strlen(p) == len) && (memcmp(p, s, len) == 0);
|
||||
}
|
||||
|
||||
uint32_t fdt_get_max_phandle(const void *fdt)
|
||||
{
|
||||
uint32_t max_phandle = 0;
|
||||
int offset;
|
||||
|
||||
for (offset = fdt_next_node(fdt, -1, NULL);;
|
||||
offset = fdt_next_node(fdt, offset, NULL)) {
|
||||
uint32_t phandle;
|
||||
|
||||
if (offset == -FDT_ERR_NOTFOUND)
|
||||
return max_phandle;
|
||||
|
||||
if (offset < 0)
|
||||
return (uint32_t)-1;
|
||||
|
||||
phandle = fdt_get_phandle(fdt, offset);
|
||||
if (phandle == (uint32_t)-1)
|
||||
continue;
|
||||
|
||||
if (phandle > max_phandle)
|
||||
max_phandle = phandle;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
|
||||
{
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
*address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
|
||||
*size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_num_mem_rsv(const void *fdt)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
|
||||
i++;
|
||||
return i;
|
||||
}
|
||||
|
||||
static int _nextprop(const void *fdt, int offset)
|
||||
{
|
||||
uint32_t tag;
|
||||
int nextoffset;
|
||||
|
||||
do {
|
||||
tag = fdt_next_tag(fdt, offset, &nextoffset);
|
||||
|
||||
switch (tag) {
|
||||
case FDT_END:
|
||||
if (nextoffset >= 0)
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
else
|
||||
return nextoffset;
|
||||
|
||||
case FDT_PROP:
|
||||
return offset;
|
||||
}
|
||||
offset = nextoffset;
|
||||
} while (tag == FDT_NOP);
|
||||
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
int fdt_subnode_offset_namelen(const void *fdt, int offset,
|
||||
const char *name, int namelen)
|
||||
{
|
||||
int depth;
|
||||
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
for (depth = 0;
|
||||
(offset >= 0) && (depth >= 0);
|
||||
offset = fdt_next_node(fdt, offset, &depth))
|
||||
if ((depth == 1)
|
||||
&& _fdt_nodename_eq(fdt, offset, name, namelen))
|
||||
return offset;
|
||||
|
||||
if (depth < 0)
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
return offset; /* error */
|
||||
}
|
||||
|
||||
int fdt_subnode_offset(const void *fdt, int parentoffset,
|
||||
const char *name)
|
||||
{
|
||||
return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
|
||||
}
|
||||
|
||||
int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
|
||||
{
|
||||
const char *end = path + namelen;
|
||||
const char *p = path;
|
||||
int offset = 0;
|
||||
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
/* see if we have an alias */
|
||||
if (*path != '/') {
|
||||
const char *q = memchr(path, '/', end - p);
|
||||
|
||||
if (!q)
|
||||
q = end;
|
||||
|
||||
p = fdt_get_alias_namelen(fdt, p, q - p);
|
||||
if (!p)
|
||||
return -FDT_ERR_BADPATH;
|
||||
offset = fdt_path_offset(fdt, p);
|
||||
|
||||
p = q;
|
||||
}
|
||||
|
||||
while (p < end) {
|
||||
const char *q;
|
||||
|
||||
while (*p == '/') {
|
||||
p++;
|
||||
if (p == end)
|
||||
return offset;
|
||||
}
|
||||
q = memchr(p, '/', end - p);
|
||||
if (! q)
|
||||
q = end;
|
||||
|
||||
offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
|
||||
if (offset < 0)
|
||||
return offset;
|
||||
|
||||
p = q;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
int fdt_path_offset(const void *fdt, const char *path)
|
||||
{
|
||||
return fdt_path_offset_namelen(fdt, path, strlen(path));
|
||||
}
|
||||
|
||||
const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
|
||||
{
|
||||
const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
|
||||
int err;
|
||||
|
||||
if (((err = fdt_check_header(fdt)) != 0)
|
||||
|| ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
|
||||
goto fail;
|
||||
|
||||
if (len)
|
||||
*len = strlen(nh->name);
|
||||
|
||||
return nh->name;
|
||||
|
||||
fail:
|
||||
if (len)
|
||||
*len = err;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fdt_first_property_offset(const void *fdt, int nodeoffset)
|
||||
{
|
||||
int offset;
|
||||
|
||||
if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
|
||||
return offset;
|
||||
|
||||
return _nextprop(fdt, offset);
|
||||
}
|
||||
|
||||
int fdt_next_property_offset(const void *fdt, int offset)
|
||||
{
|
||||
if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0)
|
||||
return offset;
|
||||
|
||||
return _nextprop(fdt, offset);
|
||||
}
|
||||
|
||||
const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
|
||||
int offset,
|
||||
int *lenp)
|
||||
{
|
||||
int err;
|
||||
const struct fdt_property *prop;
|
||||
|
||||
if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) {
|
||||
if (lenp)
|
||||
*lenp = err;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prop = _fdt_offset_ptr(fdt, offset);
|
||||
|
||||
if (lenp)
|
||||
*lenp = fdt32_to_cpu(prop->len);
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
const struct fdt_property *fdt_get_property_namelen(const void *fdt,
|
||||
int offset,
|
||||
const char *name,
|
||||
int namelen, int *lenp)
|
||||
{
|
||||
for (offset = fdt_first_property_offset(fdt, offset);
|
||||
(offset >= 0);
|
||||
(offset = fdt_next_property_offset(fdt, offset))) {
|
||||
const struct fdt_property *prop;
|
||||
|
||||
if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) {
|
||||
offset = -FDT_ERR_INTERNAL;
|
||||
break;
|
||||
}
|
||||
if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),
|
||||
name, namelen))
|
||||
return prop;
|
||||
}
|
||||
|
||||
if (lenp)
|
||||
*lenp = offset;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct fdt_property *fdt_get_property(const void *fdt,
|
||||
int nodeoffset,
|
||||
const char *name, int *lenp)
|
||||
{
|
||||
return fdt_get_property_namelen(fdt, nodeoffset, name,
|
||||
strlen(name), lenp);
|
||||
}
|
||||
|
||||
const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
|
||||
const char *name, int namelen, int *lenp)
|
||||
{
|
||||
const struct fdt_property *prop;
|
||||
|
||||
prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
|
||||
if (! prop)
|
||||
return NULL;
|
||||
|
||||
return prop->data;
|
||||
}
|
||||
|
||||
const void *fdt_getprop_by_offset(const void *fdt, int offset,
|
||||
const char **namep, int *lenp)
|
||||
{
|
||||
const struct fdt_property *prop;
|
||||
|
||||
prop = fdt_get_property_by_offset(fdt, offset, lenp);
|
||||
if (!prop)
|
||||
return NULL;
|
||||
if (namep)
|
||||
*namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
|
||||
return prop->data;
|
||||
}
|
||||
|
||||
const void *fdt_getprop(const void *fdt, int nodeoffset,
|
||||
const char *name, int *lenp)
|
||||
{
|
||||
return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
|
||||
}
|
||||
|
||||
uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
|
||||
{
|
||||
const fdt32_t *php;
|
||||
int len;
|
||||
|
||||
/* FIXME: This is a bit sub-optimal, since we potentially scan
|
||||
* over all the properties twice. */
|
||||
php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
|
||||
if (!php || (len != sizeof(*php))) {
|
||||
php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
|
||||
if (!php || (len != sizeof(*php)))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fdt32_to_cpu(*php);
|
||||
}
|
||||
|
||||
const char *fdt_get_alias_namelen(const void *fdt,
|
||||
const char *name, int namelen)
|
||||
{
|
||||
int aliasoffset;
|
||||
|
||||
aliasoffset = fdt_path_offset(fdt, "/aliases");
|
||||
if (aliasoffset < 0)
|
||||
return NULL;
|
||||
|
||||
return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
|
||||
}
|
||||
|
||||
const char *fdt_get_alias(const void *fdt, const char *name)
|
||||
{
|
||||
return fdt_get_alias_namelen(fdt, name, strlen(name));
|
||||
}
|
||||
|
||||
int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
|
||||
{
|
||||
int pdepth = 0, p = 0;
|
||||
int offset, depth, namelen;
|
||||
const char *name;
|
||||
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
if (buflen < 2)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
for (offset = 0, depth = 0;
|
||||
(offset >= 0) && (offset <= nodeoffset);
|
||||
offset = fdt_next_node(fdt, offset, &depth)) {
|
||||
while (pdepth > depth) {
|
||||
do {
|
||||
p--;
|
||||
} while (buf[p-1] != '/');
|
||||
pdepth--;
|
||||
}
|
||||
|
||||
if (pdepth >= depth) {
|
||||
name = fdt_get_name(fdt, offset, &namelen);
|
||||
if (!name)
|
||||
return namelen;
|
||||
if ((p + namelen + 1) <= buflen) {
|
||||
memcpy(buf + p, name, namelen);
|
||||
p += namelen;
|
||||
buf[p++] = '/';
|
||||
pdepth++;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset == nodeoffset) {
|
||||
if (pdepth < (depth + 1))
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
if (p > 1) /* special case so that root path is "/", not "" */
|
||||
p--;
|
||||
buf[p] = '\0';
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
else if (offset == -FDT_ERR_BADOFFSET)
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
|
||||
return offset; /* error from fdt_next_node() */
|
||||
}
|
||||
|
||||
int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
|
||||
int supernodedepth, int *nodedepth)
|
||||
{
|
||||
int offset, depth;
|
||||
int supernodeoffset = -FDT_ERR_INTERNAL;
|
||||
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
if (supernodedepth < 0)
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
|
||||
for (offset = 0, depth = 0;
|
||||
(offset >= 0) && (offset <= nodeoffset);
|
||||
offset = fdt_next_node(fdt, offset, &depth)) {
|
||||
if (depth == supernodedepth)
|
||||
supernodeoffset = offset;
|
||||
|
||||
if (offset == nodeoffset) {
|
||||
if (nodedepth)
|
||||
*nodedepth = depth;
|
||||
|
||||
if (supernodedepth > depth)
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
else
|
||||
return supernodeoffset;
|
||||
}
|
||||
}
|
||||
|
||||
if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
else if (offset == -FDT_ERR_BADOFFSET)
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
|
||||
return offset; /* error from fdt_next_node() */
|
||||
}
|
||||
|
||||
int fdt_node_depth(const void *fdt, int nodeoffset)
|
||||
{
|
||||
int nodedepth;
|
||||
int err;
|
||||
|
||||
err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
|
||||
if (err)
|
||||
return (err < 0) ? err : -FDT_ERR_INTERNAL;
|
||||
return nodedepth;
|
||||
}
|
||||
|
||||
int fdt_parent_offset(const void *fdt, int nodeoffset)
|
||||
{
|
||||
int nodedepth = fdt_node_depth(fdt, nodeoffset);
|
||||
|
||||
if (nodedepth < 0)
|
||||
return nodedepth;
|
||||
return fdt_supernode_atdepth_offset(fdt, nodeoffset,
|
||||
nodedepth - 1, NULL);
|
||||
}
|
||||
|
||||
int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
|
||||
const char *propname,
|
||||
const void *propval, int proplen)
|
||||
{
|
||||
int offset;
|
||||
const void *val;
|
||||
int len;
|
||||
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
/* FIXME: The algorithm here is pretty horrible: we scan each
|
||||
* property of a node in fdt_getprop(), then if that didn't
|
||||
* find what we want, we scan over them again making our way
|
||||
* to the next node. Still it's the easiest to implement
|
||||
* approach; performance can come later. */
|
||||
for (offset = fdt_next_node(fdt, startoffset, NULL);
|
||||
offset >= 0;
|
||||
offset = fdt_next_node(fdt, offset, NULL)) {
|
||||
val = fdt_getprop(fdt, offset, propname, &len);
|
||||
if (val && (len == proplen)
|
||||
&& (memcmp(val, propval, len) == 0))
|
||||
return offset;
|
||||
}
|
||||
|
||||
return offset; /* error from fdt_next_node() */
|
||||
}
|
||||
|
||||
int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
|
||||
{
|
||||
int offset;
|
||||
|
||||
if ((phandle == 0) || (phandle == -1))
|
||||
return -FDT_ERR_BADPHANDLE;
|
||||
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
/* FIXME: The algorithm here is pretty horrible: we
|
||||
* potentially scan each property of a node in
|
||||
* fdt_get_phandle(), then if that didn't find what
|
||||
* we want, we scan over them again making our way to the next
|
||||
* node. Still it's the easiest to implement approach;
|
||||
* performance can come later. */
|
||||
for (offset = fdt_next_node(fdt, -1, NULL);
|
||||
offset >= 0;
|
||||
offset = fdt_next_node(fdt, offset, NULL)) {
|
||||
if (fdt_get_phandle(fdt, offset) == phandle)
|
||||
return offset;
|
||||
}
|
||||
|
||||
return offset; /* error from fdt_next_node() */
|
||||
}
|
||||
|
||||
int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
|
||||
{
|
||||
int len = strlen(str);
|
||||
const char *p;
|
||||
|
||||
while (listlen >= len) {
|
||||
if (memcmp(str, strlist, len+1) == 0)
|
||||
return 1;
|
||||
p = memchr(strlist, '\0', listlen);
|
||||
if (!p)
|
||||
return 0; /* malformed strlist.. */
|
||||
listlen -= (p-strlist) + 1;
|
||||
strlist = p + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
|
||||
{
|
||||
const char *list, *end;
|
||||
int length, count = 0;
|
||||
|
||||
list = fdt_getprop(fdt, nodeoffset, property, &length);
|
||||
if (!list)
|
||||
return length;
|
||||
|
||||
end = list + length;
|
||||
|
||||
while (list < end) {
|
||||
length = strnlen(list, end - list) + 1;
|
||||
|
||||
/* Abort if the last string isn't properly NUL-terminated. */
|
||||
if (list + length > end)
|
||||
return -FDT_ERR_BADVALUE;
|
||||
|
||||
list += length;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
|
||||
const char *string)
|
||||
{
|
||||
int length, len, idx = 0;
|
||||
const char *list, *end;
|
||||
|
||||
list = fdt_getprop(fdt, nodeoffset, property, &length);
|
||||
if (!list)
|
||||
return length;
|
||||
|
||||
len = strlen(string) + 1;
|
||||
end = list + length;
|
||||
|
||||
while (list < end) {
|
||||
length = strnlen(list, end - list) + 1;
|
||||
|
||||
/* Abort if the last string isn't properly NUL-terminated. */
|
||||
if (list + length > end)
|
||||
return -FDT_ERR_BADVALUE;
|
||||
|
||||
if (length == len && memcmp(list, string, length) == 0)
|
||||
return idx;
|
||||
|
||||
list += length;
|
||||
idx++;
|
||||
}
|
||||
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
|
||||
const char *property, int idx,
|
||||
int *lenp)
|
||||
{
|
||||
const char *list, *end;
|
||||
int length;
|
||||
|
||||
list = fdt_getprop(fdt, nodeoffset, property, &length);
|
||||
if (!list) {
|
||||
if (lenp)
|
||||
*lenp = length;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
end = list + length;
|
||||
|
||||
while (list < end) {
|
||||
length = strnlen(list, end - list) + 1;
|
||||
|
||||
/* Abort if the last string isn't properly NUL-terminated. */
|
||||
if (list + length > end) {
|
||||
if (lenp)
|
||||
*lenp = -FDT_ERR_BADVALUE;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (idx == 0) {
|
||||
if (lenp)
|
||||
*lenp = length - 1;
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
list += length;
|
||||
idx--;
|
||||
}
|
||||
|
||||
if (lenp)
|
||||
*lenp = -FDT_ERR_NOTFOUND;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fdt_node_check_compatible(const void *fdt, int nodeoffset,
|
||||
const char *compatible)
|
||||
{
|
||||
const void *prop;
|
||||
int len;
|
||||
|
||||
prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
|
||||
if (!prop)
|
||||
return len;
|
||||
|
||||
return !fdt_stringlist_contains(prop, len, compatible);
|
||||
}
|
||||
|
||||
int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
|
||||
const char *compatible)
|
||||
{
|
||||
int offset, err;
|
||||
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
/* FIXME: The algorithm here is pretty horrible: we scan each
|
||||
* property of a node in fdt_node_check_compatible(), then if
|
||||
* that didn't find what we want, we scan over them again
|
||||
* making our way to the next node. Still it's the easiest to
|
||||
* implement approach; performance can come later. */
|
||||
for (offset = fdt_next_node(fdt, startoffset, NULL);
|
||||
offset >= 0;
|
||||
offset = fdt_next_node(fdt, offset, NULL)) {
|
||||
err = fdt_node_check_compatible(fdt, offset, compatible);
|
||||
if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
|
||||
return err;
|
||||
else if (err == 0)
|
||||
return offset;
|
||||
}
|
||||
|
||||
return offset; /* error from fdt_next_node() */
|
||||
}
|
489
components/drivers/fdt/libfdt/fdt_rw.c
Normal file
489
components/drivers/fdt/libfdt/fdt_rw.c
Normal file
|
@ -0,0 +1,489 @@
|
|||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
#include "fdt.h"
|
||||
#include "libfdt.h"
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
static int _fdt_blocks_misordered(const void *fdt,
|
||||
int mem_rsv_size, int struct_size)
|
||||
{
|
||||
return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8))
|
||||
|| (fdt_off_dt_struct(fdt) <
|
||||
(fdt_off_mem_rsvmap(fdt) + mem_rsv_size))
|
||||
|| (fdt_off_dt_strings(fdt) <
|
||||
(fdt_off_dt_struct(fdt) + struct_size))
|
||||
|| (fdt_totalsize(fdt) <
|
||||
(fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));
|
||||
}
|
||||
|
||||
static int _fdt_rw_check_header(void *fdt)
|
||||
{
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
if (fdt_version(fdt) < 17)
|
||||
return -FDT_ERR_BADVERSION;
|
||||
if (_fdt_blocks_misordered(fdt, sizeof(struct fdt_reserve_entry),
|
||||
fdt_size_dt_struct(fdt)))
|
||||
return -FDT_ERR_BADLAYOUT;
|
||||
if (fdt_version(fdt) > 17)
|
||||
fdt_set_version(fdt, 17);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FDT_RW_CHECK_HEADER(fdt) \
|
||||
{ \
|
||||
int __err; \
|
||||
if ((__err = _fdt_rw_check_header(fdt)) != 0) \
|
||||
return __err; \
|
||||
}
|
||||
|
||||
static inline int _fdt_data_size(void *fdt)
|
||||
{
|
||||
return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
|
||||
}
|
||||
|
||||
static int _fdt_splice(void *fdt, void *splicepoint, int oldlen, int newlen)
|
||||
{
|
||||
char *p = splicepoint;
|
||||
char *end = (char *)fdt + _fdt_data_size(fdt);
|
||||
|
||||
if (((p + oldlen) < p) || ((p + oldlen) > end))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt)))
|
||||
return -FDT_ERR_NOSPACE;
|
||||
memmove(p + newlen, p + oldlen, end - p - oldlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _fdt_splice_mem_rsv(void *fdt, struct fdt_reserve_entry *p,
|
||||
int oldn, int newn)
|
||||
{
|
||||
int delta = (newn - oldn) * sizeof(*p);
|
||||
int err;
|
||||
err = _fdt_splice(fdt, p, oldn * sizeof(*p), newn * sizeof(*p));
|
||||
if (err)
|
||||
return err;
|
||||
fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta);
|
||||
fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _fdt_splice_struct(void *fdt, void *p,
|
||||
int oldlen, int newlen)
|
||||
{
|
||||
int delta = newlen - oldlen;
|
||||
int err;
|
||||
|
||||
if ((err = _fdt_splice(fdt, p, oldlen, newlen)))
|
||||
return err;
|
||||
|
||||
fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta);
|
||||
fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _fdt_splice_string(void *fdt, int newlen)
|
||||
{
|
||||
void *p = (char *)fdt
|
||||
+ fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
|
||||
int err;
|
||||
|
||||
if ((err = _fdt_splice(fdt, p, 0, newlen)))
|
||||
return err;
|
||||
|
||||
fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _fdt_find_add_string(void *fdt, const char *s)
|
||||
{
|
||||
char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);
|
||||
const char *p;
|
||||
char *new;
|
||||
int len = strlen(s) + 1;
|
||||
int err;
|
||||
|
||||
p = _fdt_find_string(strtab, fdt_size_dt_strings(fdt), s);
|
||||
if (p)
|
||||
/* found it */
|
||||
return (p - strtab);
|
||||
|
||||
new = strtab + fdt_size_dt_strings(fdt);
|
||||
err = _fdt_splice_string(fdt, len);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memcpy(new, s, len);
|
||||
return (new - strtab);
|
||||
}
|
||||
|
||||
int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)
|
||||
{
|
||||
struct fdt_reserve_entry *re;
|
||||
int err;
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
re = _fdt_mem_rsv_w(fdt, fdt_num_mem_rsv(fdt));
|
||||
err = _fdt_splice_mem_rsv(fdt, re, 0, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
re->address = cpu_to_fdt64(address);
|
||||
re->size = cpu_to_fdt64(size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_del_mem_rsv(void *fdt, int n)
|
||||
{
|
||||
struct fdt_reserve_entry *re = _fdt_mem_rsv_w(fdt, n);
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
if (n >= fdt_num_mem_rsv(fdt))
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
|
||||
return _fdt_splice_mem_rsv(fdt, re, 1, 0);
|
||||
}
|
||||
|
||||
static int _fdt_resize_property(void *fdt, int nodeoffset, const char *name,
|
||||
int len, struct fdt_property **prop)
|
||||
{
|
||||
int oldlen;
|
||||
int err;
|
||||
|
||||
*prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
|
||||
if (! (*prop))
|
||||
return oldlen;
|
||||
|
||||
if ((err = _fdt_splice_struct(fdt, (*prop)->data, FDT_TAGALIGN(oldlen),
|
||||
FDT_TAGALIGN(len))))
|
||||
return err;
|
||||
|
||||
(*prop)->len = cpu_to_fdt32(len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _fdt_add_property(void *fdt, int nodeoffset, const char *name,
|
||||
int len, struct fdt_property **prop)
|
||||
{
|
||||
int proplen;
|
||||
int nextoffset;
|
||||
int namestroff;
|
||||
int err;
|
||||
|
||||
if ((nextoffset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
|
||||
return nextoffset;
|
||||
|
||||
namestroff = _fdt_find_add_string(fdt, name);
|
||||
if (namestroff < 0)
|
||||
return namestroff;
|
||||
|
||||
*prop = _fdt_offset_ptr_w(fdt, nextoffset);
|
||||
proplen = sizeof(**prop) + FDT_TAGALIGN(len);
|
||||
|
||||
err = _fdt_splice_struct(fdt, *prop, 0, proplen);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
(*prop)->tag = cpu_to_fdt32(FDT_PROP);
|
||||
(*prop)->nameoff = cpu_to_fdt32(namestroff);
|
||||
(*prop)->len = cpu_to_fdt32(len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_set_name(void *fdt, int nodeoffset, const char *name)
|
||||
{
|
||||
char *namep;
|
||||
int oldlen, newlen;
|
||||
int err;
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen);
|
||||
if (!namep)
|
||||
return oldlen;
|
||||
|
||||
newlen = strlen(name);
|
||||
|
||||
err = _fdt_splice_struct(fdt, namep, FDT_TAGALIGN(oldlen+1),
|
||||
FDT_TAGALIGN(newlen+1));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memcpy(namep, name, newlen+1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_setprop(void *fdt, int nodeoffset, const char *name,
|
||||
const void *val, int len)
|
||||
{
|
||||
struct fdt_property *prop;
|
||||
int err;
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
err = _fdt_resize_property(fdt, nodeoffset, name, len, &prop);
|
||||
if (err == -FDT_ERR_NOTFOUND)
|
||||
err = _fdt_add_property(fdt, nodeoffset, name, len, &prop);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (len)
|
||||
memcpy(prop->data, val, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
|
||||
const void *val, int len)
|
||||
{
|
||||
struct fdt_property *prop;
|
||||
int err, oldlen, newlen;
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
|
||||
if (prop) {
|
||||
newlen = len + oldlen;
|
||||
err = _fdt_splice_struct(fdt, prop->data,
|
||||
FDT_TAGALIGN(oldlen),
|
||||
FDT_TAGALIGN(newlen));
|
||||
if (err)
|
||||
return err;
|
||||
prop->len = cpu_to_fdt32(newlen);
|
||||
memcpy(prop->data + oldlen, val, len);
|
||||
} else {
|
||||
err = _fdt_add_property(fdt, nodeoffset, name, len, &prop);
|
||||
if (err)
|
||||
return err;
|
||||
memcpy(prop->data, val, len);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_delprop(void *fdt, int nodeoffset, const char *name)
|
||||
{
|
||||
struct fdt_property *prop;
|
||||
int len, proplen;
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
|
||||
if (! prop)
|
||||
return len;
|
||||
|
||||
proplen = sizeof(*prop) + FDT_TAGALIGN(len);
|
||||
return _fdt_splice_struct(fdt, prop, proplen, 0);
|
||||
}
|
||||
|
||||
int fdt_add_subnode_namelen(void *fdt, int parentoffset,
|
||||
const char *name, int namelen)
|
||||
{
|
||||
struct fdt_node_header *nh;
|
||||
int offset, nextoffset;
|
||||
int nodelen;
|
||||
int err;
|
||||
uint32_t tag;
|
||||
fdt32_t *endtag;
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
|
||||
if (offset >= 0)
|
||||
return -FDT_ERR_EXISTS;
|
||||
else if (offset != -FDT_ERR_NOTFOUND)
|
||||
return offset;
|
||||
|
||||
/* Try to place the new node after the parent's properties */
|
||||
fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */
|
||||
do {
|
||||
offset = nextoffset;
|
||||
tag = fdt_next_tag(fdt, offset, &nextoffset);
|
||||
} while ((tag == FDT_PROP) || (tag == FDT_NOP));
|
||||
|
||||
nh = _fdt_offset_ptr_w(fdt, offset);
|
||||
nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE;
|
||||
|
||||
err = _fdt_splice_struct(fdt, nh, 0, nodelen);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
|
||||
memset(nh->name, 0, FDT_TAGALIGN(namelen+1));
|
||||
memcpy(nh->name, name, namelen);
|
||||
endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE);
|
||||
*endtag = cpu_to_fdt32(FDT_END_NODE);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
int fdt_add_subnode(void *fdt, int parentoffset, const char *name)
|
||||
{
|
||||
return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name));
|
||||
}
|
||||
|
||||
int fdt_del_node(void *fdt, int nodeoffset)
|
||||
{
|
||||
int endoffset;
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
endoffset = _fdt_node_end_offset(fdt, nodeoffset);
|
||||
if (endoffset < 0)
|
||||
return endoffset;
|
||||
|
||||
return _fdt_splice_struct(fdt, _fdt_offset_ptr_w(fdt, nodeoffset),
|
||||
endoffset - nodeoffset, 0);
|
||||
}
|
||||
|
||||
static void _fdt_packblocks(const char *old, char *new,
|
||||
int mem_rsv_size, int struct_size)
|
||||
{
|
||||
int mem_rsv_off, struct_off, strings_off;
|
||||
|
||||
mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8);
|
||||
struct_off = mem_rsv_off + mem_rsv_size;
|
||||
strings_off = struct_off + struct_size;
|
||||
|
||||
memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size);
|
||||
fdt_set_off_mem_rsvmap(new, mem_rsv_off);
|
||||
|
||||
memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size);
|
||||
fdt_set_off_dt_struct(new, struct_off);
|
||||
fdt_set_size_dt_struct(new, struct_size);
|
||||
|
||||
memmove(new + strings_off, old + fdt_off_dt_strings(old),
|
||||
fdt_size_dt_strings(old));
|
||||
fdt_set_off_dt_strings(new, strings_off);
|
||||
fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
|
||||
}
|
||||
|
||||
int fdt_open_into(const void *fdt, void *buf, int bufsize)
|
||||
{
|
||||
int err;
|
||||
int mem_rsv_size, struct_size;
|
||||
int newsize;
|
||||
const char *fdtstart = fdt;
|
||||
const char *fdtend = fdtstart + fdt_totalsize(fdt);
|
||||
char *tmp;
|
||||
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
|
||||
* sizeof(struct fdt_reserve_entry);
|
||||
|
||||
if (fdt_version(fdt) >= 17) {
|
||||
struct_size = fdt_size_dt_struct(fdt);
|
||||
} else {
|
||||
struct_size = 0;
|
||||
while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
|
||||
;
|
||||
if (struct_size < 0)
|
||||
return struct_size;
|
||||
}
|
||||
|
||||
if (!_fdt_blocks_misordered(fdt, mem_rsv_size, struct_size)) {
|
||||
/* no further work necessary */
|
||||
err = fdt_move(fdt, buf, bufsize);
|
||||
if (err)
|
||||
return err;
|
||||
fdt_set_version(buf, 17);
|
||||
fdt_set_size_dt_struct(buf, struct_size);
|
||||
fdt_set_totalsize(buf, bufsize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Need to reorder */
|
||||
newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
|
||||
+ struct_size + fdt_size_dt_strings(fdt);
|
||||
|
||||
if (bufsize < newsize)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
/* First attempt to build converted tree at beginning of buffer */
|
||||
tmp = buf;
|
||||
/* But if that overlaps with the old tree... */
|
||||
if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) {
|
||||
/* Try right after the old tree instead */
|
||||
tmp = (char *)(uintptr_t)fdtend;
|
||||
if ((tmp + newsize) > ((char *)buf + bufsize))
|
||||
return -FDT_ERR_NOSPACE;
|
||||
}
|
||||
|
||||
_fdt_packblocks(fdt, tmp, mem_rsv_size, struct_size);
|
||||
memmove(buf, tmp, newsize);
|
||||
|
||||
fdt_set_magic(buf, FDT_MAGIC);
|
||||
fdt_set_totalsize(buf, bufsize);
|
||||
fdt_set_version(buf, 17);
|
||||
fdt_set_last_comp_version(buf, 16);
|
||||
fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_pack(void *fdt)
|
||||
{
|
||||
int mem_rsv_size;
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
|
||||
* sizeof(struct fdt_reserve_entry);
|
||||
_fdt_packblocks(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt));
|
||||
fdt_set_totalsize(fdt, _fdt_data_size(fdt));
|
||||
|
||||
return 0;
|
||||
}
|
100
components/drivers/fdt/libfdt/fdt_strerror.c
Normal file
100
components/drivers/fdt/libfdt/fdt_strerror.c
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
#include "fdt.h"
|
||||
#include "libfdt.h"
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
struct fdt_errtabent {
|
||||
const char *str;
|
||||
};
|
||||
|
||||
#define FDT_ERRTABENT(val) \
|
||||
[(val)] = { .str = #val, }
|
||||
|
||||
static struct fdt_errtabent fdt_errtable[] = {
|
||||
FDT_ERRTABENT(FDT_ERR_NOTFOUND),
|
||||
FDT_ERRTABENT(FDT_ERR_EXISTS),
|
||||
FDT_ERRTABENT(FDT_ERR_NOSPACE),
|
||||
|
||||
FDT_ERRTABENT(FDT_ERR_BADOFFSET),
|
||||
FDT_ERRTABENT(FDT_ERR_BADPATH),
|
||||
FDT_ERRTABENT(FDT_ERR_BADPHANDLE),
|
||||
FDT_ERRTABENT(FDT_ERR_BADSTATE),
|
||||
|
||||
FDT_ERRTABENT(FDT_ERR_TRUNCATED),
|
||||
FDT_ERRTABENT(FDT_ERR_BADMAGIC),
|
||||
FDT_ERRTABENT(FDT_ERR_BADVERSION),
|
||||
FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE),
|
||||
FDT_ERRTABENT(FDT_ERR_BADLAYOUT),
|
||||
FDT_ERRTABENT(FDT_ERR_INTERNAL),
|
||||
FDT_ERRTABENT(FDT_ERR_BADNCELLS),
|
||||
FDT_ERRTABENT(FDT_ERR_BADVALUE),
|
||||
FDT_ERRTABENT(FDT_ERR_BADOVERLAY),
|
||||
FDT_ERRTABENT(FDT_ERR_NOPHANDLES),
|
||||
};
|
||||
#define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0]))
|
||||
|
||||
const char *fdt_strerror(int errval)
|
||||
{
|
||||
if (errval > 0)
|
||||
return "<valid offset/length>";
|
||||
else if (errval == 0)
|
||||
return "<no error>";
|
||||
else if (errval > -FDT_ERRTABSIZE) {
|
||||
const char *s = fdt_errtable[-errval].str;
|
||||
|
||||
if (s)
|
||||
return s;
|
||||
}
|
||||
|
||||
return "<unknown error>";
|
||||
}
|
286
components/drivers/fdt/libfdt/fdt_sw.c
Normal file
286
components/drivers/fdt/libfdt/fdt_sw.c
Normal file
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
#include "fdt.h"
|
||||
#include "libfdt.h"
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
static int _fdt_sw_check_header(void *fdt)
|
||||
{
|
||||
if (fdt_magic(fdt) != FDT_SW_MAGIC)
|
||||
return -FDT_ERR_BADMAGIC;
|
||||
/* FIXME: should check more details about the header state */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FDT_SW_CHECK_HEADER(fdt) \
|
||||
{ \
|
||||
int err; \
|
||||
if ((err = _fdt_sw_check_header(fdt)) != 0) \
|
||||
return err; \
|
||||
}
|
||||
|
||||
static void *_fdt_grab_space(void *fdt, size_t len)
|
||||
{
|
||||
int offset = fdt_size_dt_struct(fdt);
|
||||
int spaceleft;
|
||||
|
||||
spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
|
||||
- fdt_size_dt_strings(fdt);
|
||||
|
||||
if ((offset + len < offset) || (offset + len > spaceleft))
|
||||
return NULL;
|
||||
|
||||
fdt_set_size_dt_struct(fdt, offset + len);
|
||||
return _fdt_offset_ptr_w(fdt, offset);
|
||||
}
|
||||
|
||||
int fdt_create(void *buf, int bufsize)
|
||||
{
|
||||
void *fdt = buf;
|
||||
|
||||
if (bufsize < sizeof(struct fdt_header))
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
memset(buf, 0, bufsize);
|
||||
|
||||
fdt_set_magic(fdt, FDT_SW_MAGIC);
|
||||
fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
|
||||
fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
|
||||
fdt_set_totalsize(fdt, bufsize);
|
||||
|
||||
fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header),
|
||||
sizeof(struct fdt_reserve_entry)));
|
||||
fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
|
||||
fdt_set_off_dt_strings(fdt, bufsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_resize(void *fdt, void *buf, int bufsize)
|
||||
{
|
||||
size_t headsize, tailsize;
|
||||
char *oldtail, *newtail;
|
||||
|
||||
FDT_SW_CHECK_HEADER(fdt);
|
||||
|
||||
headsize = fdt_off_dt_struct(fdt);
|
||||
tailsize = fdt_size_dt_strings(fdt);
|
||||
|
||||
if ((headsize + tailsize) > bufsize)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize;
|
||||
newtail = (char *)buf + bufsize - tailsize;
|
||||
|
||||
/* Two cases to avoid clobbering data if the old and new
|
||||
* buffers partially overlap */
|
||||
if (buf <= fdt) {
|
||||
memmove(buf, fdt, headsize);
|
||||
memmove(newtail, oldtail, tailsize);
|
||||
} else {
|
||||
memmove(newtail, oldtail, tailsize);
|
||||
memmove(buf, fdt, headsize);
|
||||
}
|
||||
|
||||
fdt_set_off_dt_strings(buf, bufsize);
|
||||
fdt_set_totalsize(buf, bufsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
|
||||
{
|
||||
struct fdt_reserve_entry *re;
|
||||
int offset;
|
||||
|
||||
FDT_SW_CHECK_HEADER(fdt);
|
||||
|
||||
if (fdt_size_dt_struct(fdt))
|
||||
return -FDT_ERR_BADSTATE;
|
||||
|
||||
offset = fdt_off_dt_struct(fdt);
|
||||
if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
re = (struct fdt_reserve_entry *)((char *)fdt + offset);
|
||||
re->address = cpu_to_fdt64(addr);
|
||||
re->size = cpu_to_fdt64(size);
|
||||
|
||||
fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_finish_reservemap(void *fdt)
|
||||
{
|
||||
return fdt_add_reservemap_entry(fdt, 0, 0);
|
||||
}
|
||||
|
||||
int fdt_begin_node(void *fdt, const char *name)
|
||||
{
|
||||
struct fdt_node_header *nh;
|
||||
int namelen = strlen(name) + 1;
|
||||
|
||||
FDT_SW_CHECK_HEADER(fdt);
|
||||
|
||||
nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
|
||||
if (! nh)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
|
||||
memcpy(nh->name, name, namelen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_end_node(void *fdt)
|
||||
{
|
||||
fdt32_t *en;
|
||||
|
||||
FDT_SW_CHECK_HEADER(fdt);
|
||||
|
||||
en = _fdt_grab_space(fdt, FDT_TAGSIZE);
|
||||
if (! en)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
*en = cpu_to_fdt32(FDT_END_NODE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _fdt_find_add_string(void *fdt, const char *s)
|
||||
{
|
||||
char *strtab = (char *)fdt + fdt_totalsize(fdt);
|
||||
const char *p;
|
||||
int strtabsize = fdt_size_dt_strings(fdt);
|
||||
int len = strlen(s) + 1;
|
||||
int struct_top, offset;
|
||||
|
||||
p = _fdt_find_string(strtab - strtabsize, strtabsize, s);
|
||||
if (p)
|
||||
return p - strtab;
|
||||
|
||||
/* Add it */
|
||||
offset = -strtabsize - len;
|
||||
struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
|
||||
if (fdt_totalsize(fdt) + offset < struct_top)
|
||||
return 0; /* no more room :( */
|
||||
|
||||
memcpy(strtab + offset, s, len);
|
||||
fdt_set_size_dt_strings(fdt, strtabsize + len);
|
||||
return offset;
|
||||
}
|
||||
|
||||
int fdt_property(void *fdt, const char *name, const void *val, int len)
|
||||
{
|
||||
struct fdt_property *prop;
|
||||
int nameoff;
|
||||
|
||||
FDT_SW_CHECK_HEADER(fdt);
|
||||
|
||||
nameoff = _fdt_find_add_string(fdt, name);
|
||||
if (nameoff == 0)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
|
||||
if (! prop)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
prop->tag = cpu_to_fdt32(FDT_PROP);
|
||||
prop->nameoff = cpu_to_fdt32(nameoff);
|
||||
prop->len = cpu_to_fdt32(len);
|
||||
memcpy(prop->data, val, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_finish(void *fdt)
|
||||
{
|
||||
char *p = (char *)fdt;
|
||||
fdt32_t *end;
|
||||
int oldstroffset, newstroffset;
|
||||
uint32_t tag;
|
||||
int offset, nextoffset;
|
||||
|
||||
FDT_SW_CHECK_HEADER(fdt);
|
||||
|
||||
/* Add terminator */
|
||||
end = _fdt_grab_space(fdt, sizeof(*end));
|
||||
if (! end)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
*end = cpu_to_fdt32(FDT_END);
|
||||
|
||||
/* Relocate the string table */
|
||||
oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
|
||||
newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
|
||||
memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
|
||||
fdt_set_off_dt_strings(fdt, newstroffset);
|
||||
|
||||
/* Walk the structure, correcting string offsets */
|
||||
offset = 0;
|
||||
while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
|
||||
if (tag == FDT_PROP) {
|
||||
struct fdt_property *prop =
|
||||
_fdt_offset_ptr_w(fdt, offset);
|
||||
int nameoff;
|
||||
|
||||
nameoff = fdt32_to_cpu(prop->nameoff);
|
||||
nameoff += fdt_size_dt_strings(fdt);
|
||||
prop->nameoff = cpu_to_fdt32(nameoff);
|
||||
}
|
||||
offset = nextoffset;
|
||||
}
|
||||
if (nextoffset < 0)
|
||||
return nextoffset;
|
||||
|
||||
/* Finally, adjust the header */
|
||||
fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
|
||||
fdt_set_magic(fdt, FDT_MAGIC);
|
||||
return 0;
|
||||
}
|
137
components/drivers/fdt/libfdt/fdt_wip.c
Normal file
137
components/drivers/fdt/libfdt/fdt_wip.c
Normal file
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
#include "fdt.h"
|
||||
#include "libfdt.h"
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
|
||||
const char *name, int namelen,
|
||||
uint32_t idx, const void *val,
|
||||
int len)
|
||||
{
|
||||
void *propval;
|
||||
int proplen;
|
||||
|
||||
propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen,
|
||||
&proplen);
|
||||
if (!propval)
|
||||
return proplen;
|
||||
|
||||
if (proplen < (len + idx))
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
memcpy((char *)propval + idx, val, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
|
||||
const void *val, int len)
|
||||
{
|
||||
const void *propval;
|
||||
int proplen;
|
||||
|
||||
propval = fdt_getprop(fdt, nodeoffset, name, &proplen);
|
||||
if (! propval)
|
||||
return proplen;
|
||||
|
||||
if (proplen != len)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name,
|
||||
strlen(name), 0,
|
||||
val, len);
|
||||
}
|
||||
|
||||
static void _fdt_nop_region(void *start, int len)
|
||||
{
|
||||
fdt32_t *p;
|
||||
|
||||
for (p = start; (char *)p < ((char *)start + len); p++)
|
||||
*p = cpu_to_fdt32(FDT_NOP);
|
||||
}
|
||||
|
||||
int fdt_nop_property(void *fdt, int nodeoffset, const char *name)
|
||||
{
|
||||
struct fdt_property *prop;
|
||||
int len;
|
||||
|
||||
prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
|
||||
if (! prop)
|
||||
return len;
|
||||
|
||||
_fdt_nop_region(prop, len + sizeof(*prop));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _fdt_node_end_offset(void *fdt, int offset)
|
||||
{
|
||||
int depth = 0;
|
||||
|
||||
while ((offset >= 0) && (depth >= 0))
|
||||
offset = fdt_next_node(fdt, offset, &depth);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
int fdt_nop_node(void *fdt, int nodeoffset)
|
||||
{
|
||||
int endoffset;
|
||||
|
||||
endoffset = _fdt_node_end_offset(fdt, nodeoffset);
|
||||
if (endoffset < 0)
|
||||
return endoffset;
|
||||
|
||||
_fdt_nop_region(fdt_offset_ptr_w(fdt, nodeoffset, 0),
|
||||
endoffset - nodeoffset);
|
||||
return 0;
|
||||
}
|
1883
components/drivers/fdt/libfdt/libfdt.h
Normal file
1883
components/drivers/fdt/libfdt/libfdt.h
Normal file
File diff suppressed because it is too large
Load diff
113
components/drivers/fdt/libfdt/libfdt_env.h
Normal file
113
components/drivers/fdt/libfdt/libfdt_env.h
Normal file
|
@ -0,0 +1,113 @@
|
|||
#ifndef _LIBFDT_ENV_H
|
||||
#define _LIBFDT_ENV_H
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
* Copyright 2012 Kim Phillips, Freescale Semiconductor.
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __CHECKER__
|
||||
#define FDT_FORCE __attribute__((force))
|
||||
#define FDT_BITWISE __attribute__((bitwise))
|
||||
#else
|
||||
#define FDT_FORCE
|
||||
#define FDT_BITWISE
|
||||
#endif
|
||||
|
||||
typedef uint16_t FDT_BITWISE fdt16_t;
|
||||
typedef uint32_t FDT_BITWISE fdt32_t;
|
||||
typedef uint64_t FDT_BITWISE fdt64_t;
|
||||
typedef uint64_t unaligned_fdt64_t __attribute__((aligned(4)));
|
||||
|
||||
#define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n])
|
||||
#define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1))
|
||||
#define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \
|
||||
(EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3))
|
||||
#define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \
|
||||
(EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \
|
||||
(EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \
|
||||
(EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7))
|
||||
|
||||
static inline uint16_t fdt16_to_cpu(fdt16_t x)
|
||||
{
|
||||
return (FDT_FORCE uint16_t)CPU_TO_FDT16(x);
|
||||
}
|
||||
static inline fdt16_t cpu_to_fdt16(uint16_t x)
|
||||
{
|
||||
return (FDT_FORCE fdt16_t)CPU_TO_FDT16(x);
|
||||
}
|
||||
|
||||
static inline uint32_t fdt32_to_cpu(fdt32_t x)
|
||||
{
|
||||
return (FDT_FORCE uint32_t)CPU_TO_FDT32(x);
|
||||
}
|
||||
static inline fdt32_t cpu_to_fdt32(uint32_t x)
|
||||
{
|
||||
return (FDT_FORCE fdt32_t)CPU_TO_FDT32(x);
|
||||
}
|
||||
|
||||
static inline uint64_t fdt64_to_cpu(fdt64_t x)
|
||||
{
|
||||
return (FDT_FORCE uint64_t)CPU_TO_FDT64(x);
|
||||
}
|
||||
static inline fdt64_t cpu_to_fdt64(uint64_t x)
|
||||
{
|
||||
return (FDT_FORCE fdt64_t)CPU_TO_FDT64(x);
|
||||
}
|
||||
#undef CPU_TO_FDT64
|
||||
#undef CPU_TO_FDT32
|
||||
#undef CPU_TO_FDT16
|
||||
#undef EXTRACT_BYTE
|
||||
|
||||
#endif /* _LIBFDT_ENV_H */
|
95
components/drivers/fdt/libfdt/libfdt_internal.h
Normal file
95
components/drivers/fdt/libfdt/libfdt_internal.h
Normal file
|
@ -0,0 +1,95 @@
|
|||
#ifndef _LIBFDT_INTERNAL_H
|
||||
#define _LIBFDT_INTERNAL_H
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "fdt.h"
|
||||
|
||||
#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
|
||||
#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE))
|
||||
|
||||
#define FDT_CHECK_HEADER(fdt) \
|
||||
{ \
|
||||
int __err; \
|
||||
if ((__err = fdt_check_header(fdt)) != 0) \
|
||||
return __err; \
|
||||
}
|
||||
|
||||
int _fdt_check_node_offset(const void *fdt, int offset);
|
||||
int _fdt_check_prop_offset(const void *fdt, int offset);
|
||||
const char *_fdt_find_string(const char *strtab, int tabsize, const char *s);
|
||||
int _fdt_node_end_offset(void *fdt, int nodeoffset);
|
||||
|
||||
static inline const void *_fdt_offset_ptr(const void *fdt, int offset)
|
||||
{
|
||||
return (const char *)fdt + fdt_off_dt_struct(fdt) + offset;
|
||||
}
|
||||
|
||||
static inline void *_fdt_offset_ptr_w(void *fdt, int offset)
|
||||
{
|
||||
return (void *)(uintptr_t)_fdt_offset_ptr(fdt, offset);
|
||||
}
|
||||
|
||||
static inline const struct fdt_reserve_entry *_fdt_mem_rsv(const void *fdt, int n)
|
||||
{
|
||||
const struct fdt_reserve_entry *rsv_table =
|
||||
(const struct fdt_reserve_entry *)
|
||||
((const char *)fdt + fdt_off_mem_rsvmap(fdt));
|
||||
|
||||
return rsv_table + n;
|
||||
}
|
||||
static inline struct fdt_reserve_entry *_fdt_mem_rsv_w(void *fdt, int n)
|
||||
{
|
||||
return (void *)(uintptr_t)_fdt_mem_rsv(fdt, n);
|
||||
}
|
||||
|
||||
#define FDT_SW_MAGIC (~FDT_MAGIC)
|
||||
|
||||
#endif /* _LIBFDT_INTERNAL_H */
|
17
components/drivers/fdt/src/SConscript
Normal file
17
components/drivers/fdt/src/SConscript
Normal file
|
@ -0,0 +1,17 @@
|
|||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('*.c')
|
||||
list = os.listdir(cwd)
|
||||
CPPPATH = [cwd + '/../inc' , cwd + '/libfdt']
|
||||
objs = []
|
||||
|
||||
group = DefineGroup('FDT', src, depend = ['RT_USING_FDT'], CPPPATH = CPPPATH)
|
||||
|
||||
for d in list:
|
||||
path = os.path.join(cwd, d)
|
||||
if os.path.isfile(os.path.join(path, 'SConscript')):
|
||||
objs = objs + SConscript(os.path.join(d, 'SConscript'))
|
||||
objs = objs + group
|
||||
|
||||
Return('objs')
|
648
components/drivers/fdt/src/dtb_access.c
Normal file
648
components/drivers/fdt/src/dtb_access.c
Normal file
|
@ -0,0 +1,648 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "libfdt.h"
|
||||
#include "dtb_node.h"
|
||||
|
||||
/* "/aliaes" node */
|
||||
static struct dtb_node *fdt_aliases;
|
||||
|
||||
/**
|
||||
* of_find_property_value_of_size() - find property of given size
|
||||
*
|
||||
* Search for a property in a device node and validate the requested size.
|
||||
*
|
||||
* @np: device node from which the property value is to be read.
|
||||
* @propname: name of the property to be searched.
|
||||
* @len: requested length of property value
|
||||
*
|
||||
* @return the property value on success, -EINVAL if the property does not
|
||||
* exist, -ENODATA if property does not have a value, and -EOVERFLOW if the
|
||||
* property data isn't large enough.
|
||||
*/
|
||||
static void *dtb_node_find_property_value_of_size(const struct dtb_node *dn,
|
||||
const char *propname, uint32_t len)
|
||||
{
|
||||
struct dtb_property *prop = dtb_node_get_dtb_node_property(dn, propname, NULL);
|
||||
|
||||
if (!prop)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (!prop->value)
|
||||
return ERR_PTR(-ENODATA);
|
||||
if (len > prop->size)
|
||||
return ERR_PTR(-EOVERFLOW);
|
||||
return prop->value;
|
||||
}
|
||||
|
||||
int dtb_node_read_u32(const struct dtb_node *dn, const char *propname, uint32_t *outp)
|
||||
{
|
||||
const uint32_t *val;
|
||||
|
||||
debug("%s: %s: \n", __func__, propname);
|
||||
if (!dn)
|
||||
return -EINVAL;
|
||||
val = dtb_node_find_property_value_of_size(dn, propname, sizeof(*outp));
|
||||
if (IS_ERR(val))
|
||||
{
|
||||
debug("(not found)\n");
|
||||
return PTR_ERR(val);
|
||||
}
|
||||
|
||||
*outp = fdt32_to_cpu(*val);
|
||||
debug("%#x (%d)\n", *outp, *outp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t dtb_node_read_u32_default(const struct dtb_node *node, const char *propname, uint32_t def)
|
||||
{
|
||||
dtb_node_read_u32(node, propname, &def);
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
int dtb_node_read_u32_array(const struct dtb_node *dn, const char *propname,
|
||||
uint32_t *out_values, size_t sz)
|
||||
{
|
||||
const uint32_t *val;
|
||||
|
||||
debug("%s: %s: ", __func__, propname);
|
||||
val = dtb_node_find_property_value_of_size(dn, propname,
|
||||
sz * sizeof(*out_values));
|
||||
|
||||
if (IS_ERR(val))
|
||||
return PTR_ERR(val);
|
||||
|
||||
debug("size %zd, val:%d\n", sz, *val);
|
||||
while (sz--)
|
||||
*out_values++ = fdt32_to_cpu(*val++);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t dtb_node_read_u32_index_default(const struct dtb_node *node, const char *propname, int index,
|
||||
uint32_t def)
|
||||
{
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
dtb_node_read_u32_index(node, propname, index, &def);
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
int dtb_node_read_s32_default(const struct dtb_node *node, const char *propname, int32_t def)
|
||||
{
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
dtb_node_read_u32(node, propname, (uint32_t *)&def);
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
int dtb_node_read_u32_index(const struct dtb_node *dn, const char *propname,
|
||||
int index, uint32_t *outp)
|
||||
{
|
||||
const uint32_t *val;
|
||||
|
||||
debug("%s: %s: ", __func__, propname);
|
||||
if (!dn)
|
||||
return -EINVAL;
|
||||
|
||||
val = dtb_node_find_property_value_of_size(dn, propname,
|
||||
sizeof(*outp) * (index + 1));
|
||||
if (IS_ERR(val))
|
||||
{
|
||||
debug("(not found)\n");
|
||||
return PTR_ERR(val);
|
||||
}
|
||||
|
||||
*outp = fdt32_to_cpu(val[index]);
|
||||
debug("%#x (%d)\n", *outp, *outp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dtb_node_read_u64(const struct dtb_node *dn, const char *propname, uint64_t *outp)
|
||||
{
|
||||
const uint64_t *val;
|
||||
|
||||
debug("%s: %s: ", __func__, propname);
|
||||
if (!dn)
|
||||
return -EINVAL;
|
||||
val = dtb_node_find_property_value_of_size(dn, propname, sizeof(*outp));
|
||||
if (IS_ERR(val))
|
||||
{
|
||||
debug("(not found)\n");
|
||||
return PTR_ERR(val);
|
||||
}
|
||||
|
||||
*outp = fdt64_to_cpu(*val);
|
||||
debug("%#llx (%lld)\n", (unsigned long long)*outp,
|
||||
(unsigned long long)*outp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t dtb_node_read_u64_default(const struct dtb_node *node, const char *propname, uint64_t def)
|
||||
{
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
dtb_node_read_u64(node, propname, &def);
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
int dtb_node_n_addr_cells(const struct dtb_node *dn)
|
||||
{
|
||||
const uint32_t *ip;
|
||||
|
||||
do
|
||||
{
|
||||
if (dn->parent)
|
||||
dn = dn->parent;
|
||||
ip = dtb_node_get_dtb_node_property_value(dn, "#address-cells", NULL);
|
||||
if (ip)
|
||||
return fdt32_to_cpu(*ip);
|
||||
} while (dn->parent);
|
||||
|
||||
/* No #address-cells property for the root node */
|
||||
return DEV_ROOT_NODE_ADDR_CELLS_DEFAULT;
|
||||
}
|
||||
|
||||
int dtb_node_n_size_cells(const struct dtb_node *dn)
|
||||
{
|
||||
const uint32_t *ip;
|
||||
|
||||
do
|
||||
{
|
||||
if (dn->parent)
|
||||
dn = dn->parent;
|
||||
ip = dtb_node_get_dtb_node_property_value(dn, "#size-cells", NULL);
|
||||
if (ip)
|
||||
return fdt32_to_cpu(*ip);
|
||||
} while (dn->parent);
|
||||
|
||||
/* No #size-cells property for the root node */
|
||||
return DEV_ROOT_NODE_SIZE_CELLS_DEFAULT;
|
||||
}
|
||||
|
||||
int dtb_node_simple_addr_cells(const struct dtb_node *dn)
|
||||
{
|
||||
const uint32_t *ip;
|
||||
|
||||
ip = dtb_node_get_dtb_node_property_value(dn, "#address-cells", NULL);
|
||||
if (ip)
|
||||
return fdt32_to_cpu(*ip);
|
||||
|
||||
/* Return a default of 2 to match fdt_address_cells()*/
|
||||
return 2;
|
||||
}
|
||||
|
||||
int dtb_node_simple_size_cells(const struct dtb_node *dn)
|
||||
{
|
||||
const uint32_t *ip;
|
||||
|
||||
ip = dtb_node_get_dtb_node_property_value(dn, "#size-cells", NULL);
|
||||
if (ip)
|
||||
return fdt32_to_cpu(*ip);
|
||||
|
||||
/* Return a default of 2 to match fdt_size_cells()*/
|
||||
return 2;
|
||||
}
|
||||
|
||||
struct dtb_property *dtb_node_get_dtb_node_property(const struct dtb_node *dtb_node, const char *property_name, int *property_size)
|
||||
{
|
||||
struct dtb_property *dtb_property = NULL;
|
||||
|
||||
if (dtb_node != NULL && property_name != NULL)
|
||||
{
|
||||
dtb_property = dtb_node->properties;
|
||||
|
||||
while (dtb_property != NULL)
|
||||
{
|
||||
if (!strcmp(dtb_property->name, property_name))
|
||||
{
|
||||
if (property_size != NULL)
|
||||
{
|
||||
*property_size = dtb_property->size;
|
||||
}
|
||||
return dtb_property;
|
||||
}
|
||||
dtb_property = dtb_property->next;
|
||||
}
|
||||
}
|
||||
|
||||
return dtb_property;
|
||||
}
|
||||
|
||||
#define for_each_property_of_node(dn, pp) \
|
||||
for (pp = dn->properties; pp != NULL; pp = pp->next)
|
||||
|
||||
struct dtb_node *dtb_node_find_node_opts_by_path(const char *path,
|
||||
const char **opts)
|
||||
{
|
||||
struct dtb_node *np = NULL;
|
||||
struct dtb_property *pp;
|
||||
const char *separator = strchr(path, ':');
|
||||
|
||||
if (opts)
|
||||
*opts = separator ? separator + 1 : NULL;
|
||||
|
||||
if (strcmp(path, "/") == 0)
|
||||
return dtb_node_get(get_dtb_node_head());
|
||||
|
||||
/* The path could begin with an alias */
|
||||
if (*path != '/')
|
||||
{
|
||||
int len;
|
||||
const char *p = separator;
|
||||
|
||||
if (!p)
|
||||
p = strchrnul(path, '/');
|
||||
len = p - path;
|
||||
|
||||
/* of_aliases must not be NULL */
|
||||
if (!fdt_aliases)
|
||||
return NULL;
|
||||
|
||||
for_each_property_of_node(fdt_aliases, pp)
|
||||
{
|
||||
if (strlen(pp->name) == len && !strncmp(pp->name, path,
|
||||
len))
|
||||
{
|
||||
np = dtb_node_find_node_by_path(pp->value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!np)
|
||||
return NULL;
|
||||
path = p;
|
||||
}
|
||||
|
||||
/* Step down the tree matching path components */
|
||||
if (!np)
|
||||
np = dtb_node_get(get_dtb_node_head());
|
||||
while (np && *path == '/')
|
||||
{
|
||||
struct dtb_node *tmp = np;
|
||||
|
||||
path++; /* Increment past '/' delimiter */
|
||||
np = dtb_node_get_dtb_node_by_path(np, path);
|
||||
dtb_node_put(tmp);
|
||||
path = strchrnul(path, '/');
|
||||
if (separator && separator < path)
|
||||
break;
|
||||
}
|
||||
|
||||
return np;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_find_compatible_node(struct dtb_node *from, const char *compatible)
|
||||
{
|
||||
struct dtb_node *dn;
|
||||
|
||||
for_each_of_allnodes_from(from, dn)
|
||||
{
|
||||
if (dtb_node_get_dtb_node_compatible_match(dn, compatible) &&
|
||||
dtb_node_get(dn))
|
||||
break;
|
||||
}
|
||||
dtb_node_put(from);
|
||||
|
||||
return dn;
|
||||
}
|
||||
|
||||
void *dtb_node_get_dtb_node_property_value(const struct dtb_node *dtb_node, const char *property_name, int *property_size)
|
||||
{
|
||||
struct dtb_property *dtb_property = dtb_node_get_dtb_node_property(dtb_node, property_name, NULL);
|
||||
|
||||
if (!dtb_property || !dtb_property->value || !dtb_property->size)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (property_size != NULL)
|
||||
{
|
||||
*property_size = dtb_property->size;
|
||||
}
|
||||
|
||||
return dtb_property->value;
|
||||
}
|
||||
|
||||
const struct dtb_node *dtb_node_find_node_by_prop_value(struct dtb_node *from,
|
||||
const char *propname,
|
||||
const void *propval, int proplen)
|
||||
{
|
||||
struct dtb_node *np;
|
||||
void *value;
|
||||
for_each_of_allnodes_from(from, np)
|
||||
{
|
||||
value = dtb_node_get_dtb_node_property_value(np, propname, &proplen);
|
||||
if (!memcmp(value, propval, proplen) && dtb_node_get(np))
|
||||
break;
|
||||
}
|
||||
dtb_node_put(from);
|
||||
|
||||
return np;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_find_all_nodes(const struct dtb_node *prev)
|
||||
{
|
||||
const struct dtb_node *dn;
|
||||
|
||||
if (!prev)
|
||||
{
|
||||
dn = get_dtb_node_head();
|
||||
}
|
||||
else if (prev->child)
|
||||
{
|
||||
dn = prev->child;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Walk back up looking for a sibling, or the end of the
|
||||
* structure
|
||||
*/
|
||||
dn = prev;
|
||||
while (dn->parent && !dn->sibling)
|
||||
dn = dn->parent;
|
||||
dn = dn->sibling; /* Might be null at the end of the tree */
|
||||
}
|
||||
|
||||
return (struct dtb_node *)dn;
|
||||
}
|
||||
|
||||
rt_bool_t dtb_node_device_is_available(const struct dtb_node *device)
|
||||
{
|
||||
const char *status;
|
||||
int statlen;
|
||||
|
||||
if (!device)
|
||||
return RT_FALSE;
|
||||
|
||||
status = dtb_node_get_dtb_node_property_value(device, "status", &statlen);
|
||||
if (status == NULL)
|
||||
return RT_TRUE;
|
||||
|
||||
if (statlen > 0)
|
||||
{
|
||||
if (!strcmp(status, "okay"))
|
||||
return RT_TRUE;
|
||||
}
|
||||
|
||||
return RT_FALSE;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_get_parent(const struct dtb_node *node)
|
||||
{
|
||||
const struct dtb_node *dn;
|
||||
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
dn = dtb_node_get(node->parent);
|
||||
|
||||
return (struct dtb_node *)dn;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_find_node_by_phandle(phandle handle)
|
||||
{
|
||||
struct dtb_node *dn;
|
||||
|
||||
if (!handle)
|
||||
return NULL;
|
||||
|
||||
for_each_of_allnodes(dn) if (dn->handle == handle) break;
|
||||
(void)dtb_node_get(dn);
|
||||
|
||||
return dn;
|
||||
}
|
||||
|
||||
int dtb_node_property_match_string(const struct dtb_node *dn, const char *propname,
|
||||
const char *string)
|
||||
{
|
||||
const struct dtb_property *prop = dtb_node_get_dtb_node_property(dn, propname, NULL);
|
||||
size_t l;
|
||||
int i;
|
||||
const char *p, *end;
|
||||
|
||||
if (!prop)
|
||||
return -EINVAL;
|
||||
if (!prop->value)
|
||||
return -ENODATA;
|
||||
|
||||
p = prop->value;
|
||||
end = p + prop->size;
|
||||
|
||||
for (i = 0; p < end; i++, p += l)
|
||||
{
|
||||
l = strnlen(p, end - p) + 1;
|
||||
if (p + l > end)
|
||||
return -EILSEQ;
|
||||
debug("comparing %s with %s\n", string, p);
|
||||
if (strcmp(string, p) == 0)
|
||||
return i; /* Found it; return index */
|
||||
}
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_property_read_string_helper() - Utility helper for parsing string properties
|
||||
* @np: device node from which the property value is to be read.
|
||||
* @propname: name of the property to be searched.
|
||||
* @out_strs: output array of string pointers.
|
||||
* @sz: number of array elements to read.
|
||||
* @skip: Number of strings to skip over at beginning of list.
|
||||
*
|
||||
* Don't call this function directly. It is a utility helper for the
|
||||
* of_property_read_string*() family of functions.
|
||||
*/
|
||||
int dtb_node_property_read_string_helper(const struct dtb_node *dn,
|
||||
const char *propname, const char **out_strs,
|
||||
size_t sz, int skip)
|
||||
{
|
||||
const struct dtb_property *prop = dtb_node_get_dtb_node_property(dn, propname, NULL);
|
||||
int l = 0, i = 0;
|
||||
const char *p, *end;
|
||||
|
||||
if (!prop)
|
||||
return -EINVAL;
|
||||
if (!prop->value)
|
||||
return -ENODATA;
|
||||
p = prop->value;
|
||||
end = p + prop->size;
|
||||
|
||||
for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l)
|
||||
{
|
||||
l = strnlen(p, end - p) + 1;
|
||||
if (p + l > end)
|
||||
return -EILSEQ;
|
||||
if (out_strs && i >= skip)
|
||||
*out_strs++ = p;
|
||||
}
|
||||
i -= skip;
|
||||
return i <= 0 ? -ENODATA : i;
|
||||
}
|
||||
|
||||
static int __dtb_node_parse_phandle_with_args(const struct dtb_node *dn,
|
||||
const char *list_name,
|
||||
const char *cells_name,
|
||||
int cell_count, int index,
|
||||
struct fdt_phandle_args *out_args)
|
||||
{
|
||||
const uint32_t *list, *list_end;
|
||||
int rc = 0, cur_index = 0;
|
||||
uint32_t count = 0;
|
||||
struct dtb_node *node = NULL;
|
||||
phandle phandle;
|
||||
int size;
|
||||
|
||||
/* Retrieve the phandle list property */
|
||||
list = dtb_node_get_dtb_node_property_value(dn, list_name, &size);
|
||||
if (!list)
|
||||
return -ENOENT;
|
||||
list_end = list + size / sizeof(*list);
|
||||
|
||||
/* Loop over the phandles until all the requested entry is found */
|
||||
while (list < list_end)
|
||||
{
|
||||
rc = -EINVAL;
|
||||
count = 0;
|
||||
|
||||
/*
|
||||
* If phandle is 0, then it is an empty entry with no
|
||||
* arguments. Skip forward to the next entry.
|
||||
*/
|
||||
phandle = fdt32_to_cpu(*(list++));
|
||||
if (phandle)
|
||||
{
|
||||
/*
|
||||
* Find the provider node and parse the #*-cells
|
||||
* property to determine the argument length.
|
||||
*
|
||||
* This is not needed if the cell count is hard-coded
|
||||
* (i.e. cells_name not set, but cell_count is set),
|
||||
* except when we're going to return the found node
|
||||
* below.
|
||||
*/
|
||||
if (cells_name || cur_index == index)
|
||||
{
|
||||
node = dtb_node_find_node_by_phandle(phandle);
|
||||
if (!node)
|
||||
{
|
||||
debug("%s: could not find phandle\n",
|
||||
dn->path);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (cells_name)
|
||||
{
|
||||
if (dtb_node_read_u32(node, cells_name, &count))
|
||||
{
|
||||
debug("%s: could not get %s for %s\n",
|
||||
dn->path, cells_name,
|
||||
node->path);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
count = cell_count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure that the arguments actually fit in the
|
||||
* remaining property data length
|
||||
*/
|
||||
if (list + count > list_end)
|
||||
{
|
||||
debug("%s: arguments longer than property\n",
|
||||
dn->path);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* All of the error cases above bail out of the loop, so at
|
||||
* this point, the parsing is successful. If the requested
|
||||
* index matches, then fill the out_args structure and return,
|
||||
* or return -ENOENT for an empty entry.
|
||||
*/
|
||||
rc = -ENOENT;
|
||||
if (cur_index == index)
|
||||
{
|
||||
if (!phandle)
|
||||
goto err;
|
||||
|
||||
if (out_args)
|
||||
{
|
||||
int i;
|
||||
if (count > FDT_MAX_PHANDLE_ARGS)
|
||||
count = FDT_MAX_PHANDLE_ARGS;
|
||||
out_args->np = node;
|
||||
out_args->args_count = count;
|
||||
for (i = 0; i < count; i++)
|
||||
out_args->args[i] =
|
||||
fdt32_to_cpu(*(list++));
|
||||
}
|
||||
else
|
||||
{
|
||||
dtb_node_put(node);
|
||||
}
|
||||
|
||||
/* Found it! return success */
|
||||
return 0;
|
||||
}
|
||||
|
||||
dtb_node_put(node);
|
||||
node = NULL;
|
||||
list += count;
|
||||
cur_index++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlock node before returning result; will be one of:
|
||||
* -ENOENT : index is for empty phandle
|
||||
* -EINVAL : parsing error on data
|
||||
* [1..n] : Number of phandle (count mode; when index = -1)
|
||||
*/
|
||||
rc = index < 0 ? cur_index : -ENOENT;
|
||||
err:
|
||||
if (node)
|
||||
dtb_node_put(node);
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_parse_phandle(const struct dtb_node *dn,
|
||||
const char *phandle_name, int index)
|
||||
{
|
||||
struct fdt_phandle_args args;
|
||||
|
||||
if (index < 0)
|
||||
return NULL;
|
||||
|
||||
if (__dtb_node_parse_phandle_with_args(dn, phandle_name, NULL, 0, index,
|
||||
&args))
|
||||
return NULL;
|
||||
|
||||
return args.np;
|
||||
}
|
||||
|
||||
int dtb_node_parse_phandle_with_args(const struct dtb_node *dn,
|
||||
const char *list_name, const char *cells_name,
|
||||
int index, struct fdt_phandle_args *out_args)
|
||||
{
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return __dtb_node_parse_phandle_with_args(dn, list_name, cells_name, 0,
|
||||
index, out_args);
|
||||
}
|
||||
|
||||
int dtb_node_count_phandle_with_args(const struct dtb_node *dn,
|
||||
const char *list_name, const char *cells_name)
|
||||
{
|
||||
return __dtb_node_parse_phandle_with_args(dn, list_name, cells_name, 0,
|
||||
-1, NULL);
|
||||
}
|
57
components/drivers/fdt/src/dtb_addr.c
Normal file
57
components/drivers/fdt/src/dtb_addr.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "libfdt.h"
|
||||
#include "dtb_node.h"
|
||||
|
||||
/* Max address size we deal with */
|
||||
#define FDT_MAX_ADDR_CELLS 4
|
||||
#define FDT_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= FDT_MAX_ADDR_CELLS)
|
||||
#define FDT_CHECK_COUNTS(na, ns) (FDT_CHECK_ADDR_COUNT(na) && (ns) > 0)
|
||||
|
||||
static void dtb_node_default_count_cells(const struct dtb_node *dn,
|
||||
int *addrc, int *sizec)
|
||||
{
|
||||
if (addrc)
|
||||
*addrc = dtb_node_n_addr_cells(dn);
|
||||
if (sizec)
|
||||
*sizec = dtb_node_n_size_cells(dn);
|
||||
}
|
||||
|
||||
const uint32_t *dtb_node_get_address(const struct dtb_node *dev, int index,
|
||||
uint64_t *size, unsigned int *flags)
|
||||
{
|
||||
const uint32_t *prop;
|
||||
int psize;
|
||||
struct dtb_node *parent;
|
||||
int onesize, i, na, ns;
|
||||
|
||||
/* Get parent */
|
||||
parent = dtb_node_get_parent(dev);
|
||||
if (parent == NULL)
|
||||
return NULL;
|
||||
|
||||
dtb_node_default_count_cells(dev, &na, &ns);
|
||||
if (!FDT_CHECK_ADDR_COUNT(na))
|
||||
return NULL;
|
||||
|
||||
/* Get "reg" or "assigned-addresses" property */
|
||||
prop = dtb_node_get_dtb_node_property_value(dev, "reg", &psize);
|
||||
if (prop == NULL)
|
||||
return NULL;
|
||||
psize /= 4;
|
||||
|
||||
onesize = na + ns;
|
||||
for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++)
|
||||
if (i == index)
|
||||
{
|
||||
if (size)
|
||||
*size = dtb_node_read_number(prop + na, ns);
|
||||
if (flags)
|
||||
*flags = 0x200;
|
||||
return prop;
|
||||
}
|
||||
return NULL;
|
||||
}
|
574
components/drivers/fdt/src/dtb_base.c
Normal file
574
components/drivers/fdt/src/dtb_base.c
Normal file
|
@ -0,0 +1,574 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "dtb_node.h"
|
||||
#include "libfdt.h"
|
||||
#include "libfdt_env.h"
|
||||
#include <ctype.h>
|
||||
|
||||
static const char *_parse_integer_fixup_radix(const char *s, unsigned int *base)
|
||||
{
|
||||
if (*base == 0)
|
||||
{
|
||||
if (s[0] == '0')
|
||||
{
|
||||
if (tolower(s[1]) == 'x' && isxdigit((int)s[2]))
|
||||
*base = 16;
|
||||
else
|
||||
*base = 8;
|
||||
}
|
||||
else
|
||||
*base = 10;
|
||||
}
|
||||
if (*base == 16 && s[0] == '0' && tolower(s[1]) == 'x')
|
||||
s += 2;
|
||||
return s;
|
||||
}
|
||||
|
||||
unsigned long simple_strtoul(const char *cp, char **endp,
|
||||
unsigned int base)
|
||||
{
|
||||
unsigned long result = 0;
|
||||
unsigned long value;
|
||||
|
||||
cp = _parse_integer_fixup_radix(cp, &base);
|
||||
|
||||
while (isxdigit((int)*cp) && (value = isdigit((int)*cp) ? *cp-'0' : (islower((int)*cp)
|
||||
? toupper(*cp) : *cp)-'A'+10) < base)
|
||||
{
|
||||
result = result*base + value;
|
||||
cp++;
|
||||
}
|
||||
|
||||
if (endp)
|
||||
*endp = (char *)cp;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int strict_strtoul(const char *cp, unsigned int base, unsigned long *res)
|
||||
{
|
||||
char *tail;
|
||||
unsigned long val;
|
||||
size_t len;
|
||||
|
||||
*res = 0;
|
||||
len = strlen(cp);
|
||||
if (len == 0)
|
||||
return -EINVAL;
|
||||
|
||||
val = simple_strtoul(cp, &tail, base);
|
||||
if (tail == cp)
|
||||
return -EINVAL;
|
||||
|
||||
if ((*tail == '\0') ||
|
||||
((len == (size_t)(tail - cp) + 1) && (*tail == '\n')))
|
||||
{
|
||||
*res = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
long simple_strtol(const char *cp, char **endp, unsigned int base)
|
||||
{
|
||||
if (*cp == '-')
|
||||
return -simple_strtoul(cp + 1, endp, base);
|
||||
|
||||
return simple_strtoul(cp, endp, base);
|
||||
}
|
||||
|
||||
rt_bool_t dtb_node_read_bool(const struct dtb_node *node, const char *propname)
|
||||
{
|
||||
const void *prop;
|
||||
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
debug("%s: %s: ", __func__, propname);
|
||||
|
||||
prop = dtb_node_get_property(node, propname, NULL);
|
||||
|
||||
debug("%s\n", prop ? "true" : "false");
|
||||
|
||||
return prop ? RT_TRUE : RT_FALSE;
|
||||
}
|
||||
|
||||
const void *dtb_node_read_prop(const struct dtb_node *node, const char *propname, int *sizep)
|
||||
{
|
||||
const char *val = NULL;
|
||||
int len;
|
||||
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
debug("%s: %s: ", __func__, propname);
|
||||
|
||||
struct dtb_property *prop = dtb_node_get_dtb_node_property(node, propname, &len);
|
||||
|
||||
if (prop)
|
||||
{
|
||||
val = prop->value;
|
||||
len = prop->size;
|
||||
}
|
||||
|
||||
if (!val)
|
||||
{
|
||||
debug("<not found>\n");
|
||||
if (sizep)
|
||||
*sizep = -FDT_ERR_NOTFOUND;
|
||||
return NULL;
|
||||
}
|
||||
if (sizep)
|
||||
*sizep = len;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
const char *dtb_node_read_string(const struct dtb_node *node, const char *propname)
|
||||
{
|
||||
const char *str;
|
||||
int len;
|
||||
|
||||
str = dtb_node_read_prop(node, propname, &len);
|
||||
if (!str)
|
||||
return NULL;
|
||||
|
||||
if (strnlen(str, len) >= len)
|
||||
{
|
||||
debug("<invalid>\n");
|
||||
return NULL;
|
||||
}
|
||||
debug("%s\n", str);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
const struct dtb_node *dtb_node_find_subnode(const struct dtb_node *node, const char *subnode_name)
|
||||
{
|
||||
const struct dtb_node *subnode;
|
||||
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
debug("%s: %s: ", __func__, subnode_name);
|
||||
|
||||
for (node = node->child; node; node = node->sibling)
|
||||
{
|
||||
if (!strcmp(subnode_name, node->name))
|
||||
break;
|
||||
}
|
||||
subnode = node;
|
||||
|
||||
debug("%s\n", dtb_node_valid(subnode) ?\
|
||||
dtb_node_get_name(subnode) : "<none>");
|
||||
|
||||
return subnode;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_first_subnode(const struct dtb_node *node)
|
||||
{
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
|
||||
return node->child;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_next_subnode(const struct dtb_node *node)
|
||||
{
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
|
||||
return node->sibling;
|
||||
}
|
||||
|
||||
const char *dtb_node_get_name(const struct dtb_node *node)
|
||||
{
|
||||
if (!dtb_node_valid(node))
|
||||
{
|
||||
debug("%s node not valid\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return strrchr(node->path, '/') + 1;
|
||||
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_get_by_phandle(uint32_t phandle)
|
||||
{
|
||||
if (dtb_node_active())
|
||||
return dtb_node_find_node_by_phandle(phandle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int dtb_node_read_size(const struct dtb_node *node, const char *propname)
|
||||
{
|
||||
struct dtb_property *prop = dtb_node_get_dtb_node_property( node, propname, NULL);
|
||||
|
||||
if (prop)
|
||||
return prop->size;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int dtb_node_get_addr_and_size_by_index(const struct dtb_node *node, int index, size_t *addr, size_t *size)
|
||||
{
|
||||
const uint32_t *prop;
|
||||
int psize;
|
||||
int onesize, na, ns;
|
||||
|
||||
na = dtb_node_n_addr_cells(node);
|
||||
ns = dtb_node_n_size_cells(node);
|
||||
|
||||
prop = dtb_node_get_dtb_node_property_value(node, "reg", &psize);
|
||||
if (prop == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
psize /= 4;
|
||||
onesize = na + ns;
|
||||
|
||||
if (psize >= (index + 1) * onesize)
|
||||
{
|
||||
prop += index * onesize;
|
||||
|
||||
if (addr)
|
||||
{
|
||||
*addr = dtb_node_read_number(prop, na);
|
||||
}
|
||||
if (size)
|
||||
{
|
||||
*size = dtb_node_read_number(prop + na, ns);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t dtb_node_get_addr_index(const struct dtb_node *node, int index)
|
||||
{
|
||||
int na;
|
||||
size_t size;
|
||||
|
||||
const uint32_t *prop_val;
|
||||
uint flags;
|
||||
|
||||
prop_val = dtb_node_get_address(node, index,
|
||||
(uint64_t *)&size, &flags);
|
||||
if (!prop_val)
|
||||
return -1;
|
||||
|
||||
na = dtb_node_n_addr_cells(node);
|
||||
|
||||
return dtb_node_read_number(prop_val, na);
|
||||
}
|
||||
|
||||
size_t dtb_node_get_addr(const struct dtb_node *node)
|
||||
{
|
||||
return dtb_node_get_addr_index(node, 0);
|
||||
}
|
||||
|
||||
int dtb_node_stringlist_search(const struct dtb_node *node, const char *property,
|
||||
const char *string)
|
||||
{
|
||||
return dtb_node_property_match_string(node, property, string);
|
||||
}
|
||||
|
||||
int dtb_node_read_string_index(const struct dtb_node *node, const char *property, int index,
|
||||
const char **outp)
|
||||
{
|
||||
return dtb_node_property_read_string_index(node, property, index, outp);
|
||||
}
|
||||
|
||||
int dtb_node_read_string_count(const struct dtb_node *node, const char *property)
|
||||
{
|
||||
return dtb_node_property_count_strings(node, property);
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_path(const char *path)
|
||||
{
|
||||
if (dtb_node_active())
|
||||
return dtb_node_find_node_by_path(path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *dtb_node_get_chosen_prop(const char *name)
|
||||
{
|
||||
const struct dtb_node *chosen_node;
|
||||
|
||||
chosen_node = (const struct dtb_node *)dtb_node_path("/chosen");
|
||||
|
||||
return dtb_node_read_string(chosen_node, name);
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_get_chosen_node(const char *name)
|
||||
{
|
||||
const char *prop;
|
||||
|
||||
prop = dtb_node_get_chosen_prop(name);
|
||||
if (!prop)
|
||||
return NULL;
|
||||
|
||||
return dtb_node_path(prop);
|
||||
}
|
||||
|
||||
const void *dtb_node_get_property(const struct dtb_node *node, const char *propname, int *lenp)
|
||||
{
|
||||
return dtb_node_get_dtb_node_property_value(node, propname, lenp);
|
||||
}
|
||||
|
||||
rt_bool_t dtb_node_is_available(const struct dtb_node *node)
|
||||
{
|
||||
return dtb_node_device_is_available(node);
|
||||
}
|
||||
|
||||
size_t dtb_node_get_addr_size(const struct dtb_node *node, const char *property,
|
||||
size_t *sizep)
|
||||
{
|
||||
int na, ns;
|
||||
int psize;
|
||||
const uint32_t *prop = dtb_node_get_dtb_node_property_value(node, property, &psize);
|
||||
|
||||
if (!prop)
|
||||
return -1;
|
||||
na = dtb_node_n_addr_cells(node);
|
||||
ns = dtb_node_n_size_cells(node);
|
||||
*sizep = dtb_node_read_number(prop + na, ns);
|
||||
|
||||
return dtb_node_read_number(prop, na);
|
||||
}
|
||||
|
||||
const uint8_t *dtb_node_read_u8_array_ptr(const struct dtb_node *node, const char *propname,
|
||||
size_t sz)
|
||||
{
|
||||
int psize;
|
||||
const uint32_t *prop = dtb_node_get_dtb_node_property_value(node, propname, &psize);
|
||||
|
||||
if (!prop || sz != psize)
|
||||
return NULL;
|
||||
return (uint8_t *)prop;
|
||||
}
|
||||
|
||||
int dtb_node_find_all_compatible_node(const struct dtb_node *from, const char *compatible, struct dtb_node **node_table, int max_num, int *node_num)
|
||||
{
|
||||
const struct dtb_node *dn;
|
||||
int num = 0;
|
||||
for_each_of_allnodes_from(from, dn)
|
||||
{
|
||||
if (dtb_node_get_dtb_node_compatible_match(dn, compatible) &&
|
||||
dtb_node_get(dn))
|
||||
{
|
||||
|
||||
num++;
|
||||
*node_table = (struct dtb_node *)dn;
|
||||
node_table++;
|
||||
if (num >= max_num)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*node_num = num;
|
||||
dtb_node_put(from);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dtb_node_write_prop(const struct dtb_node *node, const char *propname, int len,
|
||||
const void *value)
|
||||
{
|
||||
struct dtb_property *pp;
|
||||
struct dtb_property *pp_last = NULL;
|
||||
struct dtb_property *new;
|
||||
|
||||
if (!dtb_node_active())
|
||||
return -ENOSYS;
|
||||
|
||||
if (!node)
|
||||
return -EINVAL;
|
||||
|
||||
for (pp = node->properties; pp; pp = pp->next)
|
||||
{
|
||||
if (strcmp(pp->name, propname) == 0)
|
||||
{
|
||||
/* Property exists -> change value */
|
||||
pp->value = (void *)value;
|
||||
pp->size = len;
|
||||
return 0;
|
||||
}
|
||||
pp_last = pp;
|
||||
}
|
||||
|
||||
if (!pp_last)
|
||||
return -ENOENT;
|
||||
|
||||
/* Property does not exist -> append new property */
|
||||
new = (struct dtb_property *)malloc(sizeof(struct dtb_property));
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
new->name = strdup(propname);
|
||||
if (!new->name)
|
||||
{
|
||||
free(new);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
new->value = (void *)value;
|
||||
new->size = len;
|
||||
new->next = NULL;
|
||||
|
||||
pp_last->next = new;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dtb_node_write_string(const struct dtb_node *node, const char *propname, const char *value)
|
||||
{
|
||||
if (!dtb_node_active())
|
||||
return -ENOSYS;
|
||||
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
|
||||
debug("%s: %s = %s", __func__, propname, value);
|
||||
|
||||
return dtb_node_write_prop(node, propname, strlen(value) + 1, value);
|
||||
}
|
||||
|
||||
int dtb_node_set_enabled(const struct dtb_node *node, rt_bool_t value)
|
||||
{
|
||||
if (!dtb_node_active())
|
||||
return -ENOSYS;
|
||||
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
|
||||
if (value)
|
||||
return dtb_node_write_string(node, "status", "okay");
|
||||
else
|
||||
return dtb_node_write_string(node, "status", "disable");
|
||||
}
|
||||
|
||||
/**
|
||||
* dtb_node_irq_find_parent - Given a device node, find its interrupt parent node
|
||||
* @child: pointer to device node
|
||||
*
|
||||
* Returns a pointer to the interrupt parent node, or NULL if the interrupt
|
||||
* parent could not be determined.
|
||||
*/
|
||||
static struct dtb_node *dtb_node_irq_find_parent(struct dtb_node *child)
|
||||
{
|
||||
struct dtb_node *p;
|
||||
phandle parent;
|
||||
|
||||
if (!dtb_node_get(child))
|
||||
return NULL;
|
||||
do
|
||||
{
|
||||
if (dtb_node_read_u32_array(child, "interrupt-parent", &parent, 1))
|
||||
{
|
||||
p = dtb_node_get_parent(child);
|
||||
}
|
||||
else
|
||||
{
|
||||
p = dtb_node_get_by_phandle(parent);
|
||||
}
|
||||
dtb_node_put(child);
|
||||
child = p;
|
||||
} while (p && dtb_node_get_property(p, "#interrupt-cells", NULL) == NULL);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
int dtb_node_irq_get(struct dtb_node *dev, int index)
|
||||
{
|
||||
int rc = 0;
|
||||
struct fdt_phandle_args out_irq;
|
||||
struct dtb_node *p;
|
||||
uint32_t intsize;
|
||||
int res, i;
|
||||
|
||||
p = dtb_node_irq_find_parent(dev);
|
||||
if (p == NULL)
|
||||
return -EINVAL;
|
||||
/* Get size of interrupt specifier */
|
||||
if (dtb_node_read_u32_array(p, "#interrupt-cells", &intsize, 1))
|
||||
{
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
debug(" path:%s, parent=%pOF, intsize=%d\n", p->path,p, intsize);
|
||||
|
||||
/* Copy intspec into irq structure */
|
||||
out_irq.np = p;
|
||||
out_irq.args_count = intsize;
|
||||
for (i = 0; i < intsize; i++)
|
||||
{
|
||||
res = dtb_node_read_u32_index(dev, "interrupts",
|
||||
(index * 3 + i),
|
||||
out_irq.args + i);
|
||||
if (res)
|
||||
goto out;
|
||||
}
|
||||
rc = out_irq.args[1];
|
||||
out:
|
||||
dtb_node_put(p);
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* dtb_node_irq_get_byname - Decode a node's IRQ and return it as a Linux IRQ number
|
||||
* @dev: pointer to device tree node
|
||||
* @name: IRQ name
|
||||
*
|
||||
* Returns Linux IRQ number on success, or 0 on the IRQ mapping failure, or
|
||||
* -EPROBE_DEFER if the IRQ domain is not yet created, or error code in case
|
||||
* of any other failure.
|
||||
*/
|
||||
int dtb_node_irq_get_byname(struct dtb_node *dev, const char *name)
|
||||
{
|
||||
int index;
|
||||
|
||||
if (!name)
|
||||
return -EINVAL;
|
||||
|
||||
index = dtb_node_stringlist_search(dev, "interrupt-names", name);
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
return dtb_node_irq_get(dev, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* dtb_node_irq_count - Count the number of IRQs a node uses
|
||||
* @dev: pointer to device tree node
|
||||
*/
|
||||
int dtb_node_irq_count(struct dtb_node *device)
|
||||
{
|
||||
struct dtb_node *p;
|
||||
uint32_t intsize;
|
||||
int nr = 0, res = 0;
|
||||
|
||||
p = dtb_node_irq_find_parent(device);
|
||||
if (p == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
/* Get size of interrupt specifier */
|
||||
if (dtb_node_read_u32_array(p, "#interrupt-cells", &intsize, 1))
|
||||
{
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
debug(" path:%s, parent=%pOF, intsize=%d\n", p->path,p, intsize);
|
||||
|
||||
res = dtb_node_read_size(device, "interrupts");
|
||||
if (res < 0)
|
||||
{
|
||||
goto out;
|
||||
}
|
||||
nr = res / (intsize * 4);
|
||||
out:
|
||||
dtb_node_put(p);
|
||||
|
||||
return nr;
|
||||
}
|
820
components/drivers/fdt/src/dtb_get.c
Normal file
820
components/drivers/fdt/src/dtb_get.c
Normal file
|
@ -0,0 +1,820 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "libfdt.h"
|
||||
#include "dtb_node.h"
|
||||
|
||||
static struct
|
||||
{
|
||||
const char *ptr;
|
||||
const char *end;
|
||||
char *cur;
|
||||
} paths_buf = {NULL, NULL};
|
||||
static void *current_fdt;
|
||||
|
||||
int fdt_exec_status = FDT_RET_GET_OK;
|
||||
|
||||
int dtb_node_get_exec_status()
|
||||
{
|
||||
return fdt_exec_status;
|
||||
}
|
||||
|
||||
static int _dtb_node_get_dtb_properties_list(struct dtb_property *dtb_property, off_t node_off)
|
||||
{
|
||||
/* caller alrealy checked current_fdt */
|
||||
off_t property_off = fdt_first_property_offset(current_fdt, node_off);
|
||||
struct fdt_property *fdt_property;
|
||||
|
||||
if (property_off < 0)
|
||||
{
|
||||
return FDT_RET_GET_EMPTY;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
fdt_property = (struct fdt_property *)fdt_get_property_by_offset(current_fdt, property_off, &dtb_property->size);
|
||||
if (fdt_property != NULL)
|
||||
{
|
||||
dtb_property->name = fdt_string(current_fdt, fdt32_to_cpu(fdt_property->nameoff));
|
||||
dtb_property->value = fdt_property->data;
|
||||
dtb_property->size = fdt32_to_cpu(fdt_property->len);
|
||||
}
|
||||
|
||||
property_off = fdt_next_property_offset(current_fdt, property_off);
|
||||
if (property_off >= 0)
|
||||
{
|
||||
dtb_property->next = (struct dtb_property *)malloc(sizeof(struct dtb_property));
|
||||
if (dtb_property->next == NULL)
|
||||
{
|
||||
return FDT_RET_NO_MEMORY;
|
||||
}
|
||||
dtb_property = dtb_property->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
dtb_property->next = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return FDT_RET_GET_OK;
|
||||
}
|
||||
|
||||
static int _dtb_node_get_dtb_nodes_list(struct dtb_node *dtb_node_head, struct dtb_node *dtb_node, const char *pathname)
|
||||
{
|
||||
off_t root_off;
|
||||
off_t node_off;
|
||||
int pathname_sz;
|
||||
int node_name_sz;
|
||||
|
||||
/* caller alrealy checked current_fdt */
|
||||
if ((root_off = fdt_path_offset(current_fdt, pathname)) >= 0)
|
||||
{
|
||||
pathname_sz = strlen(pathname);
|
||||
node_off = fdt_first_subnode(current_fdt, root_off);
|
||||
|
||||
if (node_off < 0)
|
||||
{
|
||||
return FDT_RET_GET_EMPTY;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
dtb_node->parent = dtb_node_head;
|
||||
dtb_node->sibling = NULL;
|
||||
dtb_node->name = fdt_get_name(current_fdt, node_off, &node_name_sz);
|
||||
|
||||
/* parent_path + name + '/' + '\0' */
|
||||
if (paths_buf.cur + pathname_sz + node_name_sz + 2 < paths_buf.end)
|
||||
{
|
||||
dtb_node->path = (const char *)paths_buf.cur;
|
||||
strncpy(paths_buf.cur, pathname, pathname_sz);
|
||||
paths_buf.cur += pathname_sz;
|
||||
strncpy(paths_buf.cur, (char *)dtb_node->name, node_name_sz);
|
||||
paths_buf.cur += node_name_sz;
|
||||
*paths_buf.cur++ = '/';
|
||||
*paths_buf.cur++ = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
dtb_node->path = NULL;
|
||||
rt_kprintf("\033[31m\rERROR: `FDT_DTB_ALL_NODES_PATH_SIZE' = %d bytes is configured too low.\033[0m\n", FDT_DTB_ALL_NODES_PATH_SIZE);
|
||||
return FDT_RET_NO_MEMORY;
|
||||
}
|
||||
|
||||
dtb_node->handle = fdt_get_phandle(current_fdt, node_off);
|
||||
dtb_node->properties = (struct dtb_property *)malloc(sizeof(struct dtb_property));
|
||||
dtb_node->child = (struct dtb_node *)malloc(sizeof(struct dtb_node));
|
||||
|
||||
if (dtb_node->properties == NULL || dtb_node->child == NULL)
|
||||
{
|
||||
return FDT_RET_NO_MEMORY;
|
||||
}
|
||||
|
||||
fdt_exec_status = _dtb_node_get_dtb_properties_list(dtb_node->properties, node_off);
|
||||
if (fdt_exec_status == FDT_RET_GET_EMPTY)
|
||||
{
|
||||
free(dtb_node->properties);
|
||||
dtb_node->properties = NULL;
|
||||
}
|
||||
else if (fdt_exec_status != FDT_RET_GET_OK)
|
||||
{
|
||||
return fdt_exec_status;
|
||||
}
|
||||
|
||||
fdt_exec_status = _dtb_node_get_dtb_nodes_list(dtb_node, dtb_node->child, dtb_node->path);
|
||||
if (fdt_exec_status == FDT_RET_GET_EMPTY)
|
||||
{
|
||||
free(dtb_node->child);
|
||||
dtb_node->child = NULL;
|
||||
}
|
||||
else if (fdt_exec_status != FDT_RET_GET_OK)
|
||||
{
|
||||
return fdt_exec_status;
|
||||
}
|
||||
|
||||
node_off = fdt_next_subnode(current_fdt, node_off);
|
||||
if (node_off >= 0)
|
||||
{
|
||||
dtb_node->sibling = (struct dtb_node *)malloc(sizeof(struct dtb_node));
|
||||
if (dtb_node->sibling == NULL)
|
||||
{
|
||||
return FDT_RET_NO_MEMORY;
|
||||
}
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FDT_RET_GET_OK;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_get_dtb_list(void *fdt)
|
||||
{
|
||||
int root_off;
|
||||
struct dtb_node *dtb_node_head = NULL;
|
||||
|
||||
if (fdt == NULL)
|
||||
{
|
||||
fdt_exec_status = FDT_RET_NO_LOADED;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
current_fdt = fdt;
|
||||
|
||||
if ((dtb_node_head = (struct dtb_node *)malloc(sizeof(struct dtb_node))) == NULL)
|
||||
{
|
||||
fdt_exec_status = FDT_RET_NO_MEMORY;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (paths_buf.ptr == NULL)
|
||||
{
|
||||
paths_buf.ptr = (char *)malloc(FDT_DTB_ALL_NODES_PATH_SIZE);
|
||||
|
||||
if (paths_buf.ptr == NULL)
|
||||
{
|
||||
fdt_exec_status = FDT_RET_NO_MEMORY;
|
||||
goto fail;
|
||||
}
|
||||
paths_buf.cur = (char *)paths_buf.ptr;
|
||||
paths_buf.end = (char *)(paths_buf.ptr + FDT_DTB_ALL_NODES_PATH_SIZE);
|
||||
}
|
||||
|
||||
root_off = fdt_path_offset(fdt, "/");
|
||||
|
||||
if ((dtb_node_head->header = (struct dtb_header *)malloc(sizeof(struct dtb_header))) == NULL)
|
||||
|
||||
{
|
||||
fdt_exec_status = FDT_RET_NO_MEMORY;
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
((struct dtb_header *)dtb_node_head->header)->root = '/';
|
||||
((struct dtb_header *)dtb_node_head->header)->zero = '\0';
|
||||
((struct dtb_header *)dtb_node_head->header)->memreserve_sz = fdt_num_mem_rsv(fdt);
|
||||
|
||||
if (dtb_node_head->header->memreserve_sz > 0)
|
||||
{
|
||||
int i;
|
||||
int memreserve_sz = dtb_node_head->header->memreserve_sz;
|
||||
uint32_t off_mem_rsvmap = fdt_off_mem_rsvmap(fdt);
|
||||
struct fdt_reserve_entry *rsvmap = (struct fdt_reserve_entry *)((char *)fdt + off_mem_rsvmap);
|
||||
|
||||
((struct dtb_header *)dtb_node_head->header)->memreserve = (struct dtb_memreserve *)malloc(sizeof(struct dtb_memreserve) * memreserve_sz);
|
||||
if (dtb_node_head->header->memreserve == NULL)
|
||||
{
|
||||
fdt_exec_status = FDT_RET_NO_MEMORY;
|
||||
goto fail;
|
||||
}
|
||||
for (i = 0; i < memreserve_sz; ++i)
|
||||
{
|
||||
((struct dtb_header *)dtb_node_head->header)->memreserve[i].address = fdt64_to_cpu(rsvmap[i].address);
|
||||
((struct dtb_header *)dtb_node_head->header)->memreserve[i].size = fdt64_to_cpu(rsvmap[i].size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
((struct dtb_header *)dtb_node_head->header)->memreserve = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
dtb_node_head->path = paths_buf.ptr;
|
||||
*paths_buf.cur++ = '/';
|
||||
*paths_buf.cur++ = '\0';
|
||||
dtb_node_head->parent = NULL;
|
||||
dtb_node_head->sibling = NULL;
|
||||
|
||||
dtb_node_head->handle = fdt_get_phandle(fdt, root_off);
|
||||
dtb_node_head->properties = (struct dtb_property *)malloc(sizeof(struct dtb_property));
|
||||
dtb_node_head->child = (struct dtb_node *)malloc(sizeof(struct dtb_node));
|
||||
|
||||
if (dtb_node_head->properties == NULL || dtb_node_head->child == NULL)
|
||||
{
|
||||
fdt_exec_status = FDT_RET_NO_MEMORY;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((fdt_exec_status = _dtb_node_get_dtb_properties_list(dtb_node_head->properties, root_off)) != FDT_RET_GET_OK)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((fdt_exec_status = _dtb_node_get_dtb_nodes_list(dtb_node_head, dtb_node_head->child, dtb_node_head->path)) != FDT_RET_GET_OK)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* paths_buf.ptr addr save in the dtb_node_head's path */
|
||||
paths_buf.ptr = NULL;
|
||||
paths_buf.cur = NULL;
|
||||
|
||||
return dtb_node_head;
|
||||
|
||||
fail:
|
||||
if (dtb_node_head != NULL)
|
||||
{
|
||||
dtb_node_free_dtb_list(dtb_node_head);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _dtb_node_free_dtb_node(struct dtb_node *dtb_node)
|
||||
{
|
||||
struct dtb_node *dtb_node_last;
|
||||
struct dtb_property *dtb_property;
|
||||
struct dtb_property *dtb_property_last;
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
dtb_property = dtb_node->properties;
|
||||
while (dtb_property != NULL)
|
||||
{
|
||||
dtb_property_last = dtb_property;
|
||||
dtb_property = dtb_property->next;
|
||||
free(dtb_property_last);
|
||||
}
|
||||
|
||||
_dtb_node_free_dtb_node(dtb_node->child);
|
||||
|
||||
dtb_node_last = dtb_node;
|
||||
dtb_node = dtb_node->sibling;
|
||||
free(dtb_node_last);
|
||||
}
|
||||
}
|
||||
|
||||
void dtb_node_free_dtb_list(struct dtb_node *dtb_node_head)
|
||||
{
|
||||
if (dtb_node_head == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* only root node can free all path buffer */
|
||||
if (dtb_node_head->parent == NULL || (dtb_node_head->path != NULL && !strcmp(dtb_node_head->path, "/")))
|
||||
{
|
||||
if (dtb_node_head->path != NULL)
|
||||
{
|
||||
free((void *)dtb_node_head->path);
|
||||
}
|
||||
if (dtb_node_head->header != NULL)
|
||||
{
|
||||
if (dtb_node_head->header->memreserve != NULL)
|
||||
{
|
||||
free((void *)dtb_node_head->header->memreserve);
|
||||
}
|
||||
free((void *)dtb_node_head->header);
|
||||
}
|
||||
}
|
||||
|
||||
_dtb_node_free_dtb_node(dtb_node_head);
|
||||
}
|
||||
|
||||
static void _dtb_node_printf_depth(int depth)
|
||||
{
|
||||
int i = depth;
|
||||
while (i --> 0)
|
||||
{
|
||||
rt_kputs("\t");
|
||||
}
|
||||
}
|
||||
|
||||
rt_bool_t _dtb_node_test_string_list(const void *value, int size)
|
||||
{
|
||||
const char *str = value;
|
||||
const char *str_start, *str_end;
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
return RT_FALSE;
|
||||
}
|
||||
|
||||
/* string end with '\0' */
|
||||
if (str[size - 1] != '\0')
|
||||
{
|
||||
return RT_FALSE;
|
||||
}
|
||||
|
||||
/* get string list end */
|
||||
str_end = str + size;
|
||||
|
||||
while (str < str_end)
|
||||
{
|
||||
str_start = str;
|
||||
/* before string list end, not '\0' and a printable characters */
|
||||
while (str < str_end && *str && ((unsigned char)*str >= ' ' && (unsigned char)*str <= '~'))
|
||||
{
|
||||
++str;
|
||||
}
|
||||
|
||||
/* not zero, or not increased */
|
||||
if (*str != '\0' || str == str_start)
|
||||
{
|
||||
return RT_FALSE;
|
||||
}
|
||||
|
||||
/* next string */
|
||||
++str;
|
||||
}
|
||||
|
||||
return RT_TRUE;
|
||||
}
|
||||
|
||||
static void _dtb_node_printf_dtb_node_info(struct dtb_node *dtb_node)
|
||||
{
|
||||
static int depth = 0;
|
||||
struct dtb_property *dtb_property;
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
rt_kputs("\n");
|
||||
_dtb_node_printf_depth(depth);
|
||||
rt_kputs(dtb_node->name);
|
||||
rt_kputs(" {\n");
|
||||
++depth;
|
||||
|
||||
dtb_property = dtb_node->properties;
|
||||
while (dtb_property != NULL)
|
||||
{
|
||||
_dtb_node_printf_depth(depth);
|
||||
|
||||
rt_kputs(dtb_property->name);
|
||||
|
||||
if (dtb_property->size > 0)
|
||||
{
|
||||
int size = dtb_property->size;
|
||||
char *value = dtb_property->value;
|
||||
|
||||
rt_kputs(" = ");
|
||||
if (_dtb_node_test_string_list(value, size) == RT_TRUE)
|
||||
{
|
||||
/* print string list */
|
||||
char *str = value;
|
||||
do
|
||||
{
|
||||
rt_kprintf("\"%s\"", str);
|
||||
str += strlen(str) + 1;
|
||||
rt_kputs(", ");
|
||||
} while (str < value + size);
|
||||
rt_kputs("\b\b");
|
||||
}
|
||||
else if ((size % 4) == 0)
|
||||
{
|
||||
/* print addr and size cell */
|
||||
int i;
|
||||
fdt32_t *cell = (fdt32_t *)value;
|
||||
|
||||
rt_kputs("<");
|
||||
for (i = 0, size /= 4; i < size; ++i)
|
||||
{
|
||||
rt_kprintf("0x%x ", fdt32_to_cpu(cell[i]));
|
||||
}
|
||||
rt_kputs("\b>");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* print bytes array */
|
||||
int i;
|
||||
uint8_t *byte = (uint8_t *)value;
|
||||
|
||||
rt_kputs("[");
|
||||
for (i = 0; i < size; ++i)
|
||||
{
|
||||
rt_kprintf("%02x ", *byte++);
|
||||
}
|
||||
rt_kputs("\b]");
|
||||
}
|
||||
}
|
||||
rt_kputs(";\n");
|
||||
dtb_property = dtb_property->next;
|
||||
}
|
||||
|
||||
_dtb_node_printf_dtb_node_info(dtb_node->child);
|
||||
dtb_node = dtb_node->sibling;
|
||||
|
||||
--depth;
|
||||
_dtb_node_printf_depth(depth);
|
||||
rt_kputs("};\n");
|
||||
}
|
||||
}
|
||||
|
||||
void dtb_node_get_dts_dump(struct dtb_node *dtb_node_head)
|
||||
{
|
||||
if (dtb_node_head != NULL)
|
||||
{
|
||||
int i = dtb_node_head->header->memreserve_sz;
|
||||
|
||||
rt_kputs("/dts-v1/;\n");
|
||||
while (i --> 0)
|
||||
{
|
||||
rt_kprintf("\n/memreserve/\t0x%lx 0x%zx;", dtb_node_head->header->memreserve[i].address, dtb_node_head->header->memreserve[i].size);
|
||||
}
|
||||
|
||||
_dtb_node_printf_dtb_node_info(dtb_node_head);
|
||||
}
|
||||
}
|
||||
|
||||
static void _dtb_node_get_enum_dtb_node(struct dtb_node *dtb_node, void (callback(struct dtb_node *dtb_node)))
|
||||
{
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
callback(dtb_node);
|
||||
_dtb_node_get_enum_dtb_node(dtb_node->child, callback);
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
}
|
||||
|
||||
void dtb_node_get_enum_dtb_node(struct dtb_node *dtb_node_head, void (callback(struct dtb_node *dtb_node)))
|
||||
{
|
||||
if (dtb_node_head == NULL || callback == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_dtb_node_get_enum_dtb_node(dtb_node_head, callback);
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_get_dtb_node_by_name_DFS(struct dtb_node *dtb_node, const char *nodename)
|
||||
{
|
||||
struct dtb_node *dtb_node_child;
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
if (!strcmp(nodename, dtb_node->name))
|
||||
{
|
||||
return dtb_node;
|
||||
}
|
||||
|
||||
dtb_node_child = dtb_node_get_dtb_node_by_name_DFS(dtb_node->child, nodename);
|
||||
|
||||
if (dtb_node_child != NULL)
|
||||
{
|
||||
return dtb_node_child;
|
||||
}
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_get_dtb_node_by_name_BFS(struct dtb_node *dtb_node, const char *nodename)
|
||||
{
|
||||
if (dtb_node != NULL)
|
||||
{
|
||||
struct dtb_node *dtb_node_child, *dtb_node_head = dtb_node;
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
if (!strcmp(nodename, dtb_node->name))
|
||||
{
|
||||
return dtb_node;
|
||||
}
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
|
||||
dtb_node = dtb_node_head;
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
dtb_node_child = dtb_node_get_dtb_node_by_name_BFS(dtb_node->child, nodename);
|
||||
|
||||
if (dtb_node_child != NULL)
|
||||
{
|
||||
return dtb_node_child;
|
||||
}
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_get_dtb_node_by_path(struct dtb_node *dtb_node, const char *pathname)
|
||||
{
|
||||
int i = 0;
|
||||
char *node_name;
|
||||
char *pathname_clone;
|
||||
int pathname_sz;
|
||||
|
||||
if (pathname == NULL || dtb_node == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* skip '/' */
|
||||
if (*pathname == '/')
|
||||
{
|
||||
++pathname;
|
||||
}
|
||||
|
||||
/* root not have sibling, so skip */
|
||||
if (dtb_node->parent == NULL || !strcmp(dtb_node->path, "/"))
|
||||
{
|
||||
dtb_node = dtb_node->child;
|
||||
}
|
||||
|
||||
pathname_sz = strlen(pathname) + 1;
|
||||
pathname_clone = (char *)malloc(pathname_sz);
|
||||
if (pathname_clone == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
strncpy(pathname_clone, pathname, pathname_sz);
|
||||
node_name = pathname_clone;
|
||||
|
||||
while (pathname_clone[i])
|
||||
{
|
||||
if (pathname_clone[i] == '/')
|
||||
{
|
||||
/* set an end of name that can used to strcmp */
|
||||
pathname_clone[i] = '\0';
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
if (!strcmp(dtb_node->name, node_name))
|
||||
{
|
||||
break;
|
||||
}
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
if (dtb_node == NULL)
|
||||
{
|
||||
free(pathname_clone);
|
||||
return NULL;
|
||||
}
|
||||
dtb_node = dtb_node->child;
|
||||
node_name = &pathname_clone[i + 1];
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
/*
|
||||
* found the end node and it's name:
|
||||
* (pathname[pathname_sz - 1]) is '\0'
|
||||
* (&pathname_clone[i] - node_name) is the node_name's length
|
||||
*/
|
||||
node_name = &((char *)pathname)[(pathname_sz - 1) - (&pathname_clone[i] - node_name)];
|
||||
free(pathname_clone);
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
if (!strcmp(dtb_node->name, node_name))
|
||||
{
|
||||
return dtb_node;
|
||||
}
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_get_dtb_node_by_phandle_DFS(struct dtb_node *dtb_node, phandle handle)
|
||||
{
|
||||
struct dtb_node *dtb_node_child;
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
if (dtb_node->handle == handle)
|
||||
{
|
||||
return dtb_node;
|
||||
}
|
||||
|
||||
dtb_node_child = dtb_node_get_dtb_node_by_phandle_DFS(dtb_node->child, handle);
|
||||
|
||||
if (dtb_node_child != NULL)
|
||||
{
|
||||
return dtb_node_child;
|
||||
}
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_get_dtb_node_by_phandle_BFS(struct dtb_node *dtb_node, phandle handle)
|
||||
{
|
||||
if (dtb_node != NULL)
|
||||
{
|
||||
struct dtb_node *dtb_node_child, *dtb_node_head = dtb_node;
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
if (dtb_node->handle == handle)
|
||||
{
|
||||
return dtb_node;
|
||||
}
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
|
||||
dtb_node = dtb_node_head;
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
dtb_node_child = dtb_node_get_dtb_node_by_phandle_BFS(dtb_node->child, handle);
|
||||
|
||||
if (dtb_node_child != NULL)
|
||||
{
|
||||
return dtb_node_child;
|
||||
}
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dtb_node_get_dtb_node_cells(struct dtb_node *dtb_node, int *addr_cells, int *size_cells)
|
||||
{
|
||||
if (dtb_node != NULL && addr_cells != NULL && size_cells != NULL)
|
||||
{
|
||||
struct dtb_property *dtb_property;
|
||||
*addr_cells = -1;
|
||||
*size_cells = -1;
|
||||
|
||||
/* if couldn't found, check parent */
|
||||
while ((dtb_node = dtb_node->parent) != NULL)
|
||||
{
|
||||
dtb_property = dtb_node->properties;
|
||||
while (dtb_property != NULL)
|
||||
{
|
||||
if (!strcmp(dtb_property->name, "#address-cells"))
|
||||
{
|
||||
*addr_cells = fdt32_to_cpu(*(int *)dtb_property->value);
|
||||
}
|
||||
else if (!strcmp(dtb_property->name, "#size-cells"))
|
||||
{
|
||||
*size_cells = fdt32_to_cpu(*(int *)dtb_property->value);
|
||||
}
|
||||
if (*addr_cells != -1 && *size_cells != -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
dtb_property = dtb_property->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (*addr_cells == -1)
|
||||
{
|
||||
*addr_cells = FDT_ROOT_ADDR_CELLS_DEFAULT;
|
||||
}
|
||||
if (*size_cells == -1)
|
||||
{
|
||||
*size_cells = FDT_ROOT_SIZE_CELLS_DEFAULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct dtb_memreserve *dtb_node_get_dtb_memreserve(struct dtb_node *dtb_node, int *memreserve_size)
|
||||
{
|
||||
if (dtb_node != NULL && memreserve_size != NULL)
|
||||
{
|
||||
struct dtb_node *dtb_node_root = dtb_node;
|
||||
|
||||
while (dtb_node_root != NULL)
|
||||
{
|
||||
if (!strcmp(dtb_node_root->path, "/"))
|
||||
{
|
||||
break;
|
||||
}
|
||||
dtb_node_root = dtb_node_root->parent;
|
||||
}
|
||||
if(dtb_node_root == NULL) return NULL;
|
||||
|
||||
*memreserve_size = dtb_node_root->header->memreserve_sz;
|
||||
|
||||
return dtb_node_root->header->memreserve;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rt_bool_t dtb_node_get_dtb_node_status(const struct dtb_node *dtb_node)
|
||||
{
|
||||
if (dtb_node != NULL)
|
||||
{
|
||||
char *status = dtb_node_get_dtb_node_property_value(dtb_node, "status", NULL);
|
||||
if (status != NULL)
|
||||
{
|
||||
return (!strcmp(status, "okay") || !strcmp(status, "ok")) ? RT_TRUE : RT_FALSE;
|
||||
}
|
||||
|
||||
return RT_TRUE;
|
||||
}
|
||||
|
||||
return RT_FALSE;
|
||||
}
|
||||
|
||||
rt_bool_t dtb_node_get_dtb_node_compatible_match(const struct dtb_node *dtb_node, const char *compatibles)
|
||||
{
|
||||
if (dtb_node != NULL)
|
||||
{
|
||||
if (compatibles != NULL)
|
||||
{
|
||||
char *str_ptr;
|
||||
int prop_sz;
|
||||
|
||||
for_each_property_string(dtb_node, "compatible", str_ptr, prop_sz)
|
||||
{
|
||||
if (!strcmp(compatibles, str_ptr))
|
||||
{
|
||||
return RT_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RT_FALSE;
|
||||
}
|
||||
|
||||
char *dtb_node_get_dtb_string_list_value(void *value, int size, int index)
|
||||
{
|
||||
int i = 0;
|
||||
char *str = value;
|
||||
|
||||
if (str != NULL)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (i++ == index)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
str += strlen(str) + 1;
|
||||
} while (str < (char *)value + size);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *dtb_node_get_dtb_string_list_value_next(void *value, void *end)
|
||||
{
|
||||
char *str = value;
|
||||
|
||||
if (str != NULL)
|
||||
{
|
||||
str += strlen(str) + 1;
|
||||
if (str < (char *)end)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t dtb_node_get_dtb_cell_value(void *value)
|
||||
{
|
||||
return fdt32_to_cpu(*(fdt32_t *)value);
|
||||
}
|
||||
|
||||
uint8_t dtb_node_get_dtb_byte_value(void *value)
|
||||
{
|
||||
return *(uint8_t *)value;
|
||||
}
|
41
components/drivers/fdt/src/dtb_head.c
Normal file
41
components/drivers/fdt/src/dtb_head.c
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "dtb_node.h"
|
||||
|
||||
static void *dtb_root = NULL;
|
||||
static struct dtb_node *dtb_node_list = NULL;
|
||||
void *get_fdt_blob(void)
|
||||
{
|
||||
return dtb_root;
|
||||
}
|
||||
|
||||
struct dtb_node *get_dtb_node_head(void)
|
||||
{
|
||||
return dtb_node_list;
|
||||
}
|
||||
|
||||
rt_bool_t dtb_node_active(void)
|
||||
{
|
||||
return dtb_node_list != NULL;
|
||||
}
|
||||
|
||||
int device_tree_setup(void *mem_addr)
|
||||
{
|
||||
if(mem_addr)
|
||||
{
|
||||
if ((dtb_root = dtb_node_load_from_memory(mem_addr,1)) != NULL)
|
||||
{
|
||||
dtb_node_list = dtb_node_get_dtb_list(dtb_root);
|
||||
if (dtb_node_list != NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
107
components/drivers/fdt/src/dtb_load.c
Normal file
107
components/drivers/fdt/src/dtb_load.c
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "libfdt.h"
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "dtb_node.h"
|
||||
|
||||
extern int fdt_exec_status;
|
||||
|
||||
rt_bool_t dtb_node_check(void *fdt)
|
||||
{
|
||||
return fdt_check_header(fdt) == 0 ? RT_TRUE : RT_FALSE;
|
||||
}
|
||||
|
||||
void *dtb_node_load_from_fs(char *dtb_filename)
|
||||
{
|
||||
void *fdt = NULL;
|
||||
size_t dtb_sz;
|
||||
int fd = -1;
|
||||
|
||||
if (dtb_filename == NULL)
|
||||
{
|
||||
fdt_exec_status = FDT_RET_GET_EMPTY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
fd = open(dtb_filename, O_RDONLY, 0);
|
||||
|
||||
if (fd == -1)
|
||||
{
|
||||
rt_kprintf("File `%s' not found.\n", dtb_filename);
|
||||
fdt_exec_status = FDT_RET_GET_EMPTY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
dtb_sz = lseek(fd, 0, SEEK_END);
|
||||
|
||||
if (dtb_sz > 0)
|
||||
{
|
||||
if ((fdt = (struct fdt_header *)malloc(sizeof(uint8_t) * dtb_sz)) == NULL)
|
||||
{
|
||||
fdt_exec_status = FDT_RET_NO_MEMORY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
read(fd, fdt, sizeof(uint8_t) * dtb_sz);
|
||||
|
||||
if (dtb_node_check(fdt) == RT_FALSE)
|
||||
{
|
||||
free(fdt);
|
||||
fdt=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
if (fd != -1)
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return fdt;
|
||||
}
|
||||
|
||||
void *dtb_node_load_from_memory(void *dtb_ptr, rt_bool_t is_clone)
|
||||
{
|
||||
void *fdt = NULL;
|
||||
|
||||
if (dtb_ptr == NULL)
|
||||
{
|
||||
fdt_exec_status = FDT_RET_GET_EMPTY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (dtb_node_check(dtb_ptr) == RT_FALSE)
|
||||
{
|
||||
fdt_exec_status = FDT_RET_GET_EMPTY;
|
||||
fdt = NULL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (is_clone)
|
||||
{
|
||||
size_t dtb_sz = fdt_totalsize(dtb_ptr);
|
||||
if (dtb_sz > 0)
|
||||
{
|
||||
if ((fdt = (size_t *)malloc(dtb_sz)) != NULL)
|
||||
{
|
||||
memcpy(fdt, dtb_ptr, dtb_sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
fdt_exec_status = FDT_RET_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fdt = dtb_ptr;
|
||||
}
|
||||
|
||||
end:
|
||||
return fdt;
|
||||
}
|
166
components/drivers/fdt/src/dtb_set.c
Normal file
166
components/drivers/fdt/src/dtb_set.c
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "libfdt.h"
|
||||
#include "dtb_node.h"
|
||||
|
||||
static off_t dtb_node_find_and_add_subnode(void *fdt, char* name)
|
||||
{
|
||||
off_t chosen_offset = 0;
|
||||
|
||||
chosen_offset = fdt_subnode_offset(fdt, 0, name);
|
||||
|
||||
if (chosen_offset == -FDT_ERR_NOTFOUND)
|
||||
{
|
||||
chosen_offset = fdt_add_subnode(fdt, 0, name);
|
||||
}
|
||||
|
||||
return chosen_offset;
|
||||
}
|
||||
|
||||
size_t dtb_node_set_linux_cmdline(void *fdt, char *cmdline)
|
||||
{
|
||||
off_t chosen_offset;
|
||||
size_t cmdline_size;
|
||||
|
||||
if (cmdline == NULL || fdt == NULL)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
chosen_offset = dtb_node_find_and_add_subnode(fdt, "chosen");
|
||||
cmdline_size = strlen(cmdline);
|
||||
|
||||
/* install bootargs */
|
||||
if (chosen_offset >= 0 || chosen_offset == -FDT_ERR_EXISTS)
|
||||
{
|
||||
if (fdt_setprop(fdt, chosen_offset, "bootargs", cmdline, cmdline_size) < 0)
|
||||
{
|
||||
fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + FDT_DTB_PAD_SIZE);
|
||||
fdt_setprop(fdt, chosen_offset, "bootargs", cmdline, cmdline_size);
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
return fdt_totalsize(fdt);
|
||||
}
|
||||
|
||||
size_t dtb_node_set_linux_initrd(void *fdt, uint64_t initrd_addr, size_t initrd_size)
|
||||
{
|
||||
uint64_t addr, size_ptr;
|
||||
off_t chosen_offset;
|
||||
int i;
|
||||
|
||||
if (fdt == NULL)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
chosen_offset = dtb_node_find_and_add_subnode(fdt, "chosen");
|
||||
|
||||
/* update the entry */
|
||||
for (i = fdt_num_mem_rsv(fdt) - 1; i >= 0; --i)
|
||||
{
|
||||
fdt_get_mem_rsv(fdt, i, &addr, &size_ptr);
|
||||
if (addr == initrd_addr)
|
||||
{
|
||||
fdt_del_mem_rsv(fdt, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* add the memory */
|
||||
if (fdt_add_mem_rsv(fdt, initrd_addr, initrd_size) < 0)
|
||||
{
|
||||
/* move the memory */
|
||||
fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + FDT_DTB_PAD_SIZE);
|
||||
if (fdt_add_mem_rsv(fdt, initrd_addr, initrd_size) < 0)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* install initrd */
|
||||
if (chosen_offset >= 0 || chosen_offset == -FDT_ERR_EXISTS)
|
||||
{
|
||||
chosen_offset = fdt_path_offset(fdt, "/chosen");
|
||||
|
||||
if (IN_64BITS_MODE)
|
||||
{
|
||||
fdt_setprop_u64(fdt, chosen_offset, "linux,initrd-start", initrd_addr);
|
||||
fdt_setprop_u64(fdt, chosen_offset, "linux,initrd-end", initrd_addr + initrd_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
fdt_setprop_u32(fdt, chosen_offset, "linux,initrd-start", initrd_addr);
|
||||
fdt_setprop_u32(fdt, chosen_offset, "linux,initrd-end", initrd_addr + initrd_size);
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
return fdt_totalsize(fdt);
|
||||
}
|
||||
|
||||
size_t dtb_node_set_dtb_property(void *fdt, char *pathname, char *property_name, uint32_t *cells, size_t cells_size)
|
||||
{
|
||||
int node_off;
|
||||
|
||||
if (fdt == NULL)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
node_off = fdt_path_offset(fdt, pathname);
|
||||
|
||||
if (node_off >= 0 && cells_size != 0)
|
||||
{
|
||||
fdt_setprop(fdt, node_off, property_name, cells, cells_size);
|
||||
}
|
||||
|
||||
end:
|
||||
return fdt_totalsize(fdt);
|
||||
}
|
||||
|
||||
size_t dtb_node_add_dtb_memreserve(void *fdt, uint64_t address, uint64_t size)
|
||||
{
|
||||
if (fdt == NULL)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
fdt_add_mem_rsv(fdt, address, size);
|
||||
|
||||
end:
|
||||
return fdt_totalsize(fdt);
|
||||
}
|
||||
|
||||
size_t dtb_node_del_dtb_memreserve(void *fdt, uint64_t address)
|
||||
{
|
||||
int i;
|
||||
int num_mem_rsvmap;
|
||||
uint32_t off_mem_rsvmap;
|
||||
struct fdt_reserve_entry *rsvmap;
|
||||
|
||||
if (fdt == NULL)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
num_mem_rsvmap = fdt_num_mem_rsv(fdt);
|
||||
off_mem_rsvmap = fdt_off_mem_rsvmap(fdt);
|
||||
rsvmap = (struct fdt_reserve_entry *)((char *)fdt + off_mem_rsvmap);
|
||||
|
||||
for (i = 0; i < num_mem_rsvmap; ++i)
|
||||
{
|
||||
if (address == fdt64_to_cpu(rsvmap[i].address))
|
||||
{
|
||||
fdt_del_mem_rsv(fdt, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
return fdt_totalsize(fdt);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue