diff --git a/bsp/allwinner/d1/.config b/bsp/allwinner/d1/.config index 33f0866023..dbfd4b53f7 100644 --- a/bsp/allwinner/d1/.config +++ b/bsp/allwinner/d1/.config @@ -347,6 +347,7 @@ CONFIG_RT_LWIP_USING_PING=y # CONFIG_RT_USING_UTEST is not set # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/allwinner/d1/rtconfig.h b/bsp/allwinner/d1/rtconfig.h index 04fd734a30..87feff4485 100644 --- a/bsp/allwinner/d1/rtconfig.h +++ b/bsp/allwinner/d1/rtconfig.h @@ -219,6 +219,7 @@ /* Utilities */ #define RT_USING_ADT +#define RT_USING_ADT_AVL /* RT-Thread Utestcases */ diff --git a/bsp/allwinner/d1s/.config b/bsp/allwinner/d1s/.config index 28a1c0d0a5..a815cee74f 100644 --- a/bsp/allwinner/d1s/.config +++ b/bsp/allwinner/d1s/.config @@ -275,6 +275,7 @@ CONFIG_RT_USING_POSIX_PIPE_SIZE=512 # CONFIG_RT_USING_UTEST is not set # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/allwinner/d1s/rtconfig.h b/bsp/allwinner/d1s/rtconfig.h index e6e40aa64f..ccedd34d46 100644 --- a/bsp/allwinner/d1s/rtconfig.h +++ b/bsp/allwinner/d1s/rtconfig.h @@ -162,6 +162,7 @@ /* Utilities */ #define RT_USING_ADT +#define RT_USING_ADT_AVL /* RT-Thread Utestcases */ diff --git a/bsp/bouffalo_lab/bl808/d0/.config b/bsp/bouffalo_lab/bl808/d0/.config index 45c2827572..8a9e19e6b4 100644 --- a/bsp/bouffalo_lab/bl808/d0/.config +++ b/bsp/bouffalo_lab/bl808/d0/.config @@ -266,6 +266,7 @@ CONFIG_RT_USING_POSIX_PIPE_SIZE=512 # CONFIG_RT_USING_UTEST is not set # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y CONFIG_RT_USING_RESOURCE_ID=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/bouffalo_lab/bl808/d0/rtconfig.h b/bsp/bouffalo_lab/bl808/d0/rtconfig.h index ddaaf939df..9872da0e14 100755 --- a/bsp/bouffalo_lab/bl808/d0/rtconfig.h +++ b/bsp/bouffalo_lab/bl808/d0/rtconfig.h @@ -149,6 +149,7 @@ /* Utilities */ #define RT_USING_ADT +#define RT_USING_ADT_AVL #define RT_USING_RESOURCE_ID /* RT-Thread Utestcases */ diff --git a/bsp/cv1800b/.config b/bsp/cv1800b/.config index f7d25e22cf..78d9240df5 100644 --- a/bsp/cv1800b/.config +++ b/bsp/cv1800b/.config @@ -256,6 +256,7 @@ CONFIG_RT_USING_POSIX_TIMER=y # CONFIG_RT_USING_UTEST is not set # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y CONFIG_RT_USING_RESOURCE_ID=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/cv1800b/rtconfig.h b/bsp/cv1800b/rtconfig.h index ecac19cac5..43c20696a5 100644 --- a/bsp/cv1800b/rtconfig.h +++ b/bsp/cv1800b/rtconfig.h @@ -140,6 +140,7 @@ /* Utilities */ #define RT_USING_ADT +#define RT_USING_ADT_AVL #define RT_USING_RESOURCE_ID /* RT-Thread Utestcases */ diff --git a/bsp/ft2004/.config b/bsp/ft2004/.config index 942eab8345..ca770db11a 100644 --- a/bsp/ft2004/.config +++ b/bsp/ft2004/.config @@ -364,6 +364,7 @@ CONFIG_ULOG_BACKEND_USING_CONSOLE=y # CONFIG_RT_USING_UTEST is not set # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/ft2004/rtconfig.h b/bsp/ft2004/rtconfig.h index c03b9a7ddc..a4da55de52 100644 --- a/bsp/ft2004/rtconfig.h +++ b/bsp/ft2004/rtconfig.h @@ -209,6 +209,7 @@ #define ULOG_OUTPUT_TAG #define ULOG_BACKEND_USING_CONSOLE #define RT_USING_ADT +#define RT_USING_ADT_AVL /* RT-Thread Utestcases */ diff --git a/bsp/imx/imx6ull-smart/.config b/bsp/imx/imx6ull-smart/.config index c32a0179ab..f4cd26432c 100644 --- a/bsp/imx/imx6ull-smart/.config +++ b/bsp/imx/imx6ull-smart/.config @@ -414,6 +414,7 @@ CONFIG_ULOG_BACKEND_USING_CONSOLE=y # CONFIG_RT_USING_UTEST is not set # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/imx/imx6ull-smart/rtconfig.h b/bsp/imx/imx6ull-smart/rtconfig.h index d5418d96f0..b2208b9d79 100644 --- a/bsp/imx/imx6ull-smart/rtconfig.h +++ b/bsp/imx/imx6ull-smart/rtconfig.h @@ -267,6 +267,7 @@ #define ULOG_OUTPUT_TAG #define ULOG_BACKEND_USING_CONSOLE #define RT_USING_ADT +#define RT_USING_ADT_AVL /* RT-Thread Utestcases */ diff --git a/bsp/imx6ul/.config b/bsp/imx6ul/.config index fee3faa71e..7c1b500cf4 100644 --- a/bsp/imx6ul/.config +++ b/bsp/imx6ul/.config @@ -228,6 +228,7 @@ CONFIG_RT_LIBC_DEFAULT_TIMEZONE=8 # CONFIG_RT_USING_UTEST is not set # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/imx6ul/rtconfig.h b/bsp/imx6ul/rtconfig.h index 833168b1d3..3310b75945 100644 --- a/bsp/imx6ul/rtconfig.h +++ b/bsp/imx6ul/rtconfig.h @@ -113,6 +113,7 @@ /* Utilities */ #define RT_USING_ADT +#define RT_USING_ADT_AVL /* RT-Thread Utestcases */ diff --git a/bsp/nuvoton/nk-980iot/.config b/bsp/nuvoton/nk-980iot/.config index 5dddc30467..184978b323 100644 --- a/bsp/nuvoton/nk-980iot/.config +++ b/bsp/nuvoton/nk-980iot/.config @@ -398,6 +398,7 @@ CONFIG_UTEST_THR_STACK_SIZE=4096 CONFIG_UTEST_THR_PRIORITY=20 # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/nuvoton/nk-n9h30/.config b/bsp/nuvoton/nk-n9h30/.config index cc86ef62ae..89ffdfe1c0 100644 --- a/bsp/nuvoton/nk-n9h30/.config +++ b/bsp/nuvoton/nk-n9h30/.config @@ -386,6 +386,7 @@ CONFIG_UTEST_THR_STACK_SIZE=4096 CONFIG_UTEST_THR_PRIORITY=20 # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/nuvoton/nk-rtu980/.config b/bsp/nuvoton/nk-rtu980/.config index 74f9055cca..13df539149 100644 --- a/bsp/nuvoton/nk-rtu980/.config +++ b/bsp/nuvoton/nk-rtu980/.config @@ -404,6 +404,7 @@ CONFIG_UTEST_THR_STACK_SIZE=4096 CONFIG_UTEST_THR_PRIORITY=20 # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/nuvoton/numaker-hmi-ma35d1/.config b/bsp/nuvoton/numaker-hmi-ma35d1/.config index d063bd93a0..6df82b3414 100644 --- a/bsp/nuvoton/numaker-hmi-ma35d1/.config +++ b/bsp/nuvoton/numaker-hmi-ma35d1/.config @@ -398,6 +398,7 @@ CONFIG_UTEST_THR_STACK_SIZE=4096 CONFIG_UTEST_THR_PRIORITY=20 # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/nuvoton/numaker-iot-ma35d1/.config b/bsp/nuvoton/numaker-iot-ma35d1/.config index d7eeccde32..e630741b8e 100644 --- a/bsp/nuvoton/numaker-iot-ma35d1/.config +++ b/bsp/nuvoton/numaker-iot-ma35d1/.config @@ -399,6 +399,7 @@ CONFIG_UTEST_THR_STACK_SIZE=4096 CONFIG_UTEST_THR_PRIORITY=20 # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/phytium/aarch32/.config b/bsp/phytium/aarch32/.config index e78d576898..62d8bff4b0 100644 --- a/bsp/phytium/aarch32/.config +++ b/bsp/phytium/aarch32/.config @@ -272,6 +272,7 @@ CONFIG_UTEST_THR_STACK_SIZE=4096 CONFIG_UTEST_THR_PRIORITY=20 # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/phytium/aarch32/rtconfig.h b/bsp/phytium/aarch32/rtconfig.h index 913e053bd7..4146488599 100644 --- a/bsp/phytium/aarch32/rtconfig.h +++ b/bsp/phytium/aarch32/rtconfig.h @@ -164,6 +164,7 @@ #define UTEST_THR_STACK_SIZE 4096 #define UTEST_THR_PRIORITY 20 #define RT_USING_ADT +#define RT_USING_ADT_AVL /* RT-Thread Utestcases */ diff --git a/bsp/phytium/aarch64/.config b/bsp/phytium/aarch64/.config index c63a196b1f..24e339bf7c 100644 --- a/bsp/phytium/aarch64/.config +++ b/bsp/phytium/aarch64/.config @@ -252,6 +252,7 @@ CONFIG_YMODEM_USING_FILE_TRANSFER=y # CONFIG_RT_USING_UTEST is not set # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/phytium/aarch64/rtconfig.h b/bsp/phytium/aarch64/rtconfig.h index 76b14cfc28..29e11f836e 100644 --- a/bsp/phytium/aarch64/rtconfig.h +++ b/bsp/phytium/aarch64/rtconfig.h @@ -143,6 +143,7 @@ #define RT_USING_RYM #define YMODEM_USING_FILE_TRANSFER #define RT_USING_ADT +#define RT_USING_ADT_AVL /* RT-Thread Utestcases */ diff --git a/bsp/qemu-vexpress-a9/.config b/bsp/qemu-vexpress-a9/.config index a90d2217e6..d0b510d289 100644 --- a/bsp/qemu-vexpress-a9/.config +++ b/bsp/qemu-vexpress-a9/.config @@ -289,6 +289,7 @@ CONFIG_RT_USING_POSIX_MESSAGE_SEMAPHORE=y # CONFIG_RT_USING_UTEST is not set # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/qemu-vexpress-a9/rtconfig.h b/bsp/qemu-vexpress-a9/rtconfig.h index 99c4fa425a..42af02ceb6 100644 --- a/bsp/qemu-vexpress-a9/rtconfig.h +++ b/bsp/qemu-vexpress-a9/rtconfig.h @@ -181,6 +181,7 @@ /* Utilities */ #define RT_USING_ADT +#define RT_USING_ADT_AVL /* RT-Thread Utestcases */ diff --git a/bsp/qemu-virt64-aarch64/.config b/bsp/qemu-virt64-aarch64/.config index 57c2735f2e..86d73fe1c0 100644 --- a/bsp/qemu-virt64-aarch64/.config +++ b/bsp/qemu-virt64-aarch64/.config @@ -277,6 +277,7 @@ CONFIG_RT_USING_POSIX_PIPE_SIZE=512 # CONFIG_RT_USING_UTEST is not set # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/qemu-virt64-aarch64/rtconfig.h b/bsp/qemu-virt64-aarch64/rtconfig.h index 4e9dd72e7a..3bdf25b998 100644 --- a/bsp/qemu-virt64-aarch64/rtconfig.h +++ b/bsp/qemu-virt64-aarch64/rtconfig.h @@ -171,6 +171,7 @@ /* Utilities */ #define RT_USING_ADT +#define RT_USING_ADT_AVL /* RT-Thread Utestcases */ diff --git a/bsp/qemu-virt64-riscv/.config b/bsp/qemu-virt64-riscv/.config index c7da62a3dd..571d1fdfe0 100644 --- a/bsp/qemu-virt64-riscv/.config +++ b/bsp/qemu-virt64-riscv/.config @@ -349,6 +349,7 @@ CONFIG_UTEST_THR_STACK_SIZE=4096 CONFIG_UTEST_THR_PRIORITY=20 # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/qemu-virt64-riscv/rtconfig.h b/bsp/qemu-virt64-riscv/rtconfig.h index 1741274ead..2d174cf9d7 100644 --- a/bsp/qemu-virt64-riscv/rtconfig.h +++ b/bsp/qemu-virt64-riscv/rtconfig.h @@ -223,6 +223,7 @@ #define UTEST_THR_STACK_SIZE 4096 #define UTEST_THR_PRIORITY 20 #define RT_USING_ADT +#define RT_USING_ADT_AVL /* RT-Thread Utestcases */ diff --git a/bsp/raspberry-pi/raspi2/.config b/bsp/raspberry-pi/raspi2/.config index ac188aedbf..6f25dab649 100644 --- a/bsp/raspberry-pi/raspi2/.config +++ b/bsp/raspberry-pi/raspi2/.config @@ -218,6 +218,7 @@ CONFIG_RT_LIBC_DEFAULT_TIMEZONE=8 # CONFIG_RT_USING_UTEST is not set # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/raspberry-pi/raspi2/rtconfig.h b/bsp/raspberry-pi/raspi2/rtconfig.h index 2ebdb977e4..48deaf9aef 100644 --- a/bsp/raspberry-pi/raspi2/rtconfig.h +++ b/bsp/raspberry-pi/raspi2/rtconfig.h @@ -107,6 +107,7 @@ /* Utilities */ #define RT_USING_ADT +#define RT_USING_ADT_AVL /* RT-Thread Utestcases */ diff --git a/bsp/raspberry-pi/raspi3-32/.config b/bsp/raspberry-pi/raspi3-32/.config index 8779d3bfd9..2b02db83db 100644 --- a/bsp/raspberry-pi/raspi3-32/.config +++ b/bsp/raspberry-pi/raspi3-32/.config @@ -260,6 +260,7 @@ CONFIG_RT_LIBC_DEFAULT_TIMEZONE=8 # CONFIG_RT_USING_UTEST is not set # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/raspberry-pi/raspi3-32/rtconfig.h b/bsp/raspberry-pi/raspi3-32/rtconfig.h index 2ab676eec4..b3567095ae 100644 --- a/bsp/raspberry-pi/raspi3-32/rtconfig.h +++ b/bsp/raspberry-pi/raspi3-32/rtconfig.h @@ -138,6 +138,7 @@ /* Utilities */ #define RT_USING_ADT +#define RT_USING_ADT_AVL /* RT-Thread Utestcases */ diff --git a/bsp/raspberry-pi/raspi3-64/.config b/bsp/raspberry-pi/raspi3-64/.config index e26421abfa..01eb5c7dfe 100644 --- a/bsp/raspberry-pi/raspi3-64/.config +++ b/bsp/raspberry-pi/raspi3-64/.config @@ -265,6 +265,7 @@ CONFIG_RT_LIBC_DEFAULT_TIMEZONE=8 # CONFIG_RT_USING_UTEST is not set # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/raspberry-pi/raspi3-64/rtconfig.h b/bsp/raspberry-pi/raspi3-64/rtconfig.h index b9aedc17b5..ab319ae256 100644 --- a/bsp/raspberry-pi/raspi3-64/rtconfig.h +++ b/bsp/raspberry-pi/raspi3-64/rtconfig.h @@ -148,6 +148,7 @@ /* Utilities */ #define RT_USING_ADT +#define RT_USING_ADT_AVL /* RT-Thread Utestcases */ diff --git a/bsp/raspberry-pi/raspi4-32/.config b/bsp/raspberry-pi/raspi4-32/.config index 0cf47e124a..26dfa85e75 100644 --- a/bsp/raspberry-pi/raspi4-32/.config +++ b/bsp/raspberry-pi/raspi4-32/.config @@ -350,6 +350,7 @@ CONFIG_RT_LWIP_USING_PING=y # CONFIG_RT_USING_UTEST is not set # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/raspberry-pi/raspi4-32/rtconfig.h b/bsp/raspberry-pi/raspi4-32/rtconfig.h index fd394f3d69..f946b29294 100644 --- a/bsp/raspberry-pi/raspi4-32/rtconfig.h +++ b/bsp/raspberry-pi/raspi4-32/rtconfig.h @@ -204,6 +204,7 @@ /* Utilities */ #define RT_USING_ADT +#define RT_USING_ADT_AVL /* RT-Thread Utestcases */ diff --git a/bsp/raspberry-pi/raspi4-64/.config b/bsp/raspberry-pi/raspi4-64/.config index 7228a42d85..e8668e9f36 100644 --- a/bsp/raspberry-pi/raspi4-64/.config +++ b/bsp/raspberry-pi/raspi4-64/.config @@ -357,6 +357,7 @@ CONFIG_UTEST_THR_STACK_SIZE=8192 CONFIG_UTEST_THR_PRIORITY=20 # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/raspberry-pi/raspi4-64/rtconfig.h b/bsp/raspberry-pi/raspi4-64/rtconfig.h index ff4b54bd7e..d84361cb25 100644 --- a/bsp/raspberry-pi/raspi4-64/rtconfig.h +++ b/bsp/raspberry-pi/raspi4-64/rtconfig.h @@ -227,6 +227,7 @@ #define UTEST_THR_STACK_SIZE 8192 #define UTEST_THR_PRIORITY 20 #define RT_USING_ADT +#define RT_USING_ADT_AVL /* RT-Thread Utestcases */ diff --git a/bsp/rockchip/rk3568/.config b/bsp/rockchip/rk3568/.config index 772d3c6469..5e0e0893da 100644 --- a/bsp/rockchip/rk3568/.config +++ b/bsp/rockchip/rk3568/.config @@ -216,6 +216,7 @@ CONFIG_RT_LIBC_DEFAULT_TIMEZONE=8 # CONFIG_RT_USING_UTEST is not set # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_VBUS is not set diff --git a/bsp/rockchip/rk3568/rtconfig.h b/bsp/rockchip/rk3568/rtconfig.h index 38fd8b3407..54931231c2 100644 --- a/bsp/rockchip/rk3568/rtconfig.h +++ b/bsp/rockchip/rk3568/rtconfig.h @@ -113,6 +113,7 @@ /* Utilities */ #define RT_USING_ADT +#define RT_USING_ADT_AVL /* RT-Thread Utestcases */ diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig index 92a7dbf6ff..4a3d7f7fff 100755 --- a/components/drivers/Kconfig +++ b/components/drivers/Kconfig @@ -773,6 +773,9 @@ menuconfig RT_USING_VIRTIO default y endif +source "$RTT_DIR/components/drivers/ofw/Kconfig" +source "$RTT_DIR/components/drivers/pic/Kconfig" + menu "Using USB" config RT_USING_USB bool diff --git a/components/drivers/include/drivers/ofw.h b/components/drivers/include/drivers/ofw.h new file mode 100644 index 0000000000..c760cf7e83 --- /dev/null +++ b/components/drivers/include/drivers/ofw.h @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-25 GuEe-GUI first version + */ + +#ifndef __OFW_H__ +#define __OFW_H__ + +#include + +#include +#include + +#include + +typedef rt_uint32_t rt_phandle; + +struct rt_ofw_prop +{ + const char *name; + int length; + void *value; + + struct rt_ofw_prop *next; +}; + +struct rt_ofw_node +{ + const char *name; + /* full_name is 'path/tag' or 'path/tag@reg' */ + const char *full_name; + /* phandles range from 1 to 2^32-2 (0xfffffffe) */ + rt_phandle phandle; + + struct rt_ofw_prop *props; + struct rt_ofw_node *parent; + struct rt_ofw_node *child; + struct rt_ofw_node *sibling; + + struct rt_ref ref; + +#define RT_OFW_F_SYSTEM 0 /* node is system node */ +#define RT_OFW_F_READLY 1 /* node has driver */ +#define RT_OFW_F_PLATFORM 2 /* node is platform device */ +#define RT_OFW_F_OVERLAY 3 /* node is from overlay */ + rt_bitmap_t flags; + + /* RT-Thread object prototype */ + void *rt_data; +}; + +#define RT_OFW_MAX_CELL_ARGS 16 + +struct rt_ofw_cell_args +{ + void *data; + + int args_count; + rt_uint32_t args[RT_OFW_MAX_CELL_ARGS]; +}; + +struct rt_ofw_node_id +{ + /* The name string should consist name property (deprecated) */ + char name[32]; + + /* + * The type string should consist device_type property, such as pci, memory + * serial. Because it's deprecated in , we can use other + * name (like "ttyS" or "ttyAMA" ...) to config with /chosen. + */ + char type[32]; + + /* + * The compatible string should consist only of lowercase letters, digits + * and dashes, and should start with a letter. A single comma is typically + * only used following a vendor prefix. Underscores should not be used. + */ + char compatible[128]; + + const void *data; +}; + +struct rt_ofw_stub +{ + const struct rt_ofw_node_id *ids; + rt_err_t (*handler)(struct rt_ofw_node *np, const struct rt_ofw_node_id *id); +}; + +#define RT_OFW_SYMBOL(_class, _level) \ + rt_section(".rt_ofw_data." #_class "." #_level) + +#define RT_OFW_SYMBOL_TYPE_RANGE(_class, _type, _start, _end) \ +static const rt_used RT_OFW_SYMBOL(_class, 0) _type _start; \ +static const rt_used RT_OFW_SYMBOL(_class, end) _type _end; \ + +#define RT_OFW_STUB_EXPORT(_name, _ids, _class, _handler, ...) \ +static const struct rt_ofw_stub __rt_ofw_##_name \ +rt_used RT_OFW_SYMBOL(_class, __VA_ARGS__ _) = \ +{ \ + .ids = _ids, \ + .handler = _handler, \ +} + +#define RT_OFW_STUB_RANGE_EXPORT(_class, _start, _end) \ + RT_OFW_SYMBOL_TYPE_RANGE(_class, struct rt_ofw_stub, _start = {}, _end = {}) + +#define rt_ofw_data(np) ((struct rt_ofw_node *)np)->rt_data + +rt_inline rt_bool_t rt_ofw_node_test_flag(const struct rt_ofw_node *np, int flag) +{ + return rt_bitmap_test_bit((rt_bitmap_t *)&np->flags, flag); +} + +rt_inline void rt_ofw_node_set_flag(struct rt_ofw_node *np, int flag) +{ + rt_bitmap_set_bit(&np->flags, flag); +} + +rt_inline rt_bool_t rt_ofw_node_test_and_set_flag(struct rt_ofw_node *np, int flag) +{ + rt_bool_t res = rt_ofw_node_test_flag(np, flag); + + rt_ofw_node_set_flag(np, flag); + + return res; +} + +rt_inline void rt_ofw_node_clear_flag(struct rt_ofw_node *np, int flag) +{ + rt_bitmap_clear_bit(&np->flags, flag); +} + +rt_err_t rt_ofw_node_destroy(struct rt_ofw_node *np); + +struct rt_ofw_node *rt_ofw_node_get(struct rt_ofw_node *np); +void rt_ofw_node_put(struct rt_ofw_node *np); + +rt_bool_t rt_ofw_node_tag_equ(const struct rt_ofw_node *np, const char *tag); +rt_bool_t rt_ofw_node_tag_prefix(const struct rt_ofw_node *np, const char *prefix); + +rt_inline const char *rt_ofw_node_name(const struct rt_ofw_node *np) +{ + return np ? np->name : ""; +} + +rt_inline const char *rt_ofw_node_full_name(const struct rt_ofw_node *np) +{ + return np ? np->full_name : ""; +} + +rt_bool_t rt_ofw_machine_is_compatible(const char *compatible); +rt_bool_t rt_ofw_node_is_available(const struct rt_ofw_node *np); +rt_bool_t rt_ofw_node_is_compatible(const struct rt_ofw_node *np, const char *compatible); + +struct rt_ofw_node_id *rt_ofw_prop_match(struct rt_ofw_prop *prop, const struct rt_ofw_node_id *ids); +struct rt_ofw_node_id *rt_ofw_node_match(struct rt_ofw_node *np, const struct rt_ofw_node_id *ids); + +struct rt_ofw_node *rt_ofw_find_node_by_tag(struct rt_ofw_node *from, const char *tag); +struct rt_ofw_node *rt_ofw_find_node_by_prop_r(struct rt_ofw_node *from, const char *propname, + const struct rt_ofw_prop **out_prop); + +rt_inline struct rt_ofw_node *rt_ofw_find_node_by_prop(struct rt_ofw_node *from, const char *propname) +{ + return rt_ofw_find_node_by_prop_r(from, propname, RT_NULL); +} + +struct rt_ofw_node *rt_ofw_find_node_by_name(struct rt_ofw_node *from, const char *name); +struct rt_ofw_node *rt_ofw_find_node_by_type(struct rt_ofw_node *from, const char *type); +struct rt_ofw_node *rt_ofw_find_node_by_compatible(struct rt_ofw_node *from, const char *compatible); +struct rt_ofw_node *rt_ofw_find_node_by_ids_r(struct rt_ofw_node *from, const struct rt_ofw_node_id *ids, + const struct rt_ofw_node_id **out_id); +struct rt_ofw_node *rt_ofw_find_node_by_path(const char *path); +struct rt_ofw_node *rt_ofw_find_node_by_phandle(rt_phandle phandle); + +rt_inline struct rt_ofw_node *rt_ofw_find_node_by_ids(struct rt_ofw_node *from, const struct rt_ofw_node_id *ids) +{ + return rt_ofw_find_node_by_ids_r(from, ids, RT_NULL); +} + +struct rt_ofw_node *rt_ofw_get_parent(const struct rt_ofw_node *np); +struct rt_ofw_node *rt_ofw_get_child_by_tag(const struct rt_ofw_node *parent, const char *tag); +struct rt_ofw_node *rt_ofw_get_child_by_compatible(const struct rt_ofw_node *parent, const char *compatible); + +int rt_ofw_get_child_count(const struct rt_ofw_node *np); +int rt_ofw_get_available_child_count(const struct rt_ofw_node *np); + +struct rt_ofw_node *rt_ofw_get_next_node(struct rt_ofw_node *prev); +struct rt_ofw_node *rt_ofw_get_next_parent(struct rt_ofw_node *prev); +struct rt_ofw_node *rt_ofw_get_next_child(const struct rt_ofw_node *parent, struct rt_ofw_node *prev); +struct rt_ofw_node *rt_ofw_get_next_available_child(const struct rt_ofw_node *parent, struct rt_ofw_node *prev); + +struct rt_ofw_node *rt_ofw_get_cpu_node(int cpu, int *thread, rt_bool_t (*match_cpu_hwid)(int cpu, rt_uint64_t hwid)); +struct rt_ofw_node *rt_ofw_get_next_cpu_node(struct rt_ofw_node *prev); +struct rt_ofw_node *rt_ofw_get_cpu_state_node(struct rt_ofw_node *cpu_np, int index); +rt_uint64_t rt_ofw_get_cpu_id(struct rt_ofw_node *cpu_np); +rt_uint64_t rt_ofw_get_cpu_hwid(struct rt_ofw_node *cpu_np, unsigned int thread); + +struct rt_ofw_node *rt_ofw_get_alias_node(const char *tag, int id); +int rt_ofw_get_alias_id(struct rt_ofw_node *np, const char *tag); +int rt_ofw_get_alias_last_id(const char *tag); + +struct rt_ofw_node *rt_ofw_parse_phandle(const struct rt_ofw_node *np, const char *phandle_name, int index); +rt_err_t rt_ofw_parse_phandle_cells(const struct rt_ofw_node *np, const char *list_name, const char *cells_name, + int index, struct rt_ofw_cell_args *out_args); +int rt_ofw_count_phandle_cells(const struct rt_ofw_node *np, const char *list_name, const char *cells_name); + +struct rt_ofw_prop *rt_ofw_get_prop(const struct rt_ofw_node *np, const char *name, rt_ssize_t *out_length); + +rt_inline const void *rt_ofw_prop_read_raw(const struct rt_ofw_node *np, const char *name, rt_ssize_t *out_length) +{ + struct rt_ofw_prop *prop = rt_ofw_get_prop(np, name, out_length); + + return prop ? prop->value : RT_NULL; +} + +int rt_ofw_prop_read_u8_array_index(const struct rt_ofw_node *np, const char *propname, + int index, int nr, rt_uint8_t *out_values); +int rt_ofw_prop_read_u16_array_index(const struct rt_ofw_node *np, const char *propname, + int index, int nr, rt_uint16_t *out_values); +int rt_ofw_prop_read_u32_array_index(const struct rt_ofw_node *np, const char *propname, + int index, int nr, rt_uint32_t *out_values); +int rt_ofw_prop_read_u64_array_index(const struct rt_ofw_node *np, const char *propname, + int index, int nr, rt_uint64_t *out_values); +int rt_ofw_prop_read_string_array_index(const struct rt_ofw_node *np, const char *propname, + int index, int nr, const char **out_strings); + +int rt_ofw_prop_count_of_size(const struct rt_ofw_node *np, const char *propname, int size); +int rt_ofw_prop_index_of_string(const struct rt_ofw_node *np, const char *propname, const char *string); + +const fdt32_t *rt_ofw_prop_next_u32(struct rt_ofw_prop *prop, const fdt32_t *cur, rt_uint32_t *out_value); +const char *rt_ofw_prop_next_string(struct rt_ofw_prop *prop, const char *cur); + +rt_inline rt_err_t rt_ofw_prop_read_u8_index(const struct rt_ofw_node *np, const char *propname, + int index, rt_uint8_t *out_value) +{ + int nr = rt_ofw_prop_read_u8_array_index(np, propname, index, 1, out_value); + + return nr > 0 ? RT_EOK : (rt_err_t)nr; +} + +rt_inline rt_err_t rt_ofw_prop_read_u16_index(const struct rt_ofw_node *np, const char *propname, + int index, rt_uint16_t *out_value) +{ + int nr = rt_ofw_prop_read_u16_array_index(np, propname, index, 1, out_value); + + return nr > 0 ? RT_EOK : (rt_err_t)nr; +} + +rt_inline rt_err_t rt_ofw_prop_read_u32_index(const struct rt_ofw_node *np, const char *propname, + int index, rt_uint32_t *out_value) +{ + int nr = rt_ofw_prop_read_u32_array_index(np, propname, index, 1, out_value); + + return nr > 0 ? RT_EOK : (rt_err_t)nr; +} + +rt_inline rt_err_t rt_ofw_prop_read_u64_index(const struct rt_ofw_node *np, const char *propname, + int index, rt_uint64_t *out_value) +{ + int nr = rt_ofw_prop_read_u64_array_index(np, propname, index, 1, out_value); + + return nr > 0 ? RT_EOK : (rt_err_t)nr; +} + +rt_inline rt_err_t rt_ofw_prop_read_string_index(const struct rt_ofw_node *np, const char *propname, + int index, const char **out_string) +{ + int nr = rt_ofw_prop_read_string_array_index(np, propname, index, 1, out_string); + + return nr > 0 ? RT_EOK : (rt_err_t)nr; +} + +rt_inline rt_err_t rt_ofw_prop_read_u8(const struct rt_ofw_node *np, const char *propname, + rt_uint8_t *out_value) +{ + return rt_ofw_prop_read_u8_index(np, propname, 0, out_value); +} + +rt_inline rt_err_t rt_ofw_prop_read_u16(const struct rt_ofw_node *np, const char *propname, + rt_uint16_t *out_value) +{ + return rt_ofw_prop_read_u16_index(np, propname, 0, out_value); +} + +rt_inline rt_err_t rt_ofw_prop_read_u32(const struct rt_ofw_node *np, const char *propname, + rt_uint32_t *out_value) +{ + return rt_ofw_prop_read_u32_index(np, propname, 0, out_value); +} + +rt_inline rt_err_t rt_ofw_prop_read_s32(const struct rt_ofw_node *np, const char *propname, + rt_int32_t *out_value) +{ + return rt_ofw_prop_read_u32_index(np, propname, 0, (rt_uint32_t *)out_value); +} + +rt_inline rt_err_t rt_ofw_prop_read_u64(const struct rt_ofw_node *np, const char *propname, + rt_uint64_t *out_value) +{ + return rt_ofw_prop_read_u64_index(np, propname, 0, out_value); +} + +rt_inline rt_err_t rt_ofw_prop_read_string(const struct rt_ofw_node *np, const char *propname, + const char **out_string) +{ + return rt_ofw_prop_read_string_index(np, propname, 0, out_string); +} + +rt_inline rt_bool_t rt_ofw_prop_read_bool(const struct rt_ofw_node *np, const char *propname) +{ + return rt_ofw_get_prop(np, propname, RT_NULL) ? RT_TRUE : RT_FALSE; +} + +rt_inline int rt_ofw_prop_count_of_u8(const struct rt_ofw_node *np, const char *propname) +{ + return rt_ofw_prop_count_of_size(np, propname, sizeof(rt_uint8_t)); +} + +rt_inline int rt_ofw_prop_count_of_u16(const struct rt_ofw_node *np, const char *propname) +{ + return rt_ofw_prop_count_of_size(np, propname, sizeof(rt_uint16_t)); +} + +rt_inline int rt_ofw_prop_count_of_u32(const struct rt_ofw_node *np, const char *propname) +{ + return rt_ofw_prop_count_of_size(np, propname, sizeof(rt_uint32_t)); +} + +rt_inline int rt_ofw_prop_count_of_u64(const struct rt_ofw_node *np, const char *propname) +{ + return rt_ofw_prop_count_of_size(np, propname, sizeof(rt_uint64_t)); +} + +rt_inline const char *rt_ofw_node_type(const struct rt_ofw_node *np) +{ + return rt_ofw_prop_read_raw(np, "device_type", RT_NULL); +} + +rt_inline rt_bool_t rt_ofw_node_is_type(const struct rt_ofw_node *np, const char *type) +{ + const char *get_type = rt_ofw_node_type(np); + + return np && get_type && type && !rt_strcmp(get_type, type); +} + +#define rt_ofw_foreach_node_by_tag(np, name) \ + for (np = rt_ofw_find_node_by_tag(RT_NULL, name); np; \ + np = rt_ofw_find_node_by_tag(np, name)) + +#define rt_ofw_foreach_node_by_prop(np, prop_name) \ + for (np = rt_ofw_find_node_by_prop(RT_NULL, prop_name); \ + np; np = rt_ofw_find_node_by_prop(np, prop_name)) + +#define rt_ofw_foreach_node_by_prop_r(np, prop_name, prop) \ + for (np = rt_ofw_find_node_by_prop_r(RT_NULL, prop_name, prop); \ + np; np = rt_ofw_find_node_by_prop_r(np, prop_name, prop)) + +#define rt_ofw_foreach_node_by_name(np, name) \ + for (np = rt_ofw_find_node_by_name(RT_NULL, name); np; \ + np = rt_ofw_find_node_by_name(np, name)) + +#define rt_ofw_foreach_node_by_type(np, type) \ + for (np = rt_ofw_find_node_by_type(RT_NULL, type); np; \ + np = rt_ofw_find_node_by_type(np, type)) + +#define rt_ofw_foreach_node_by_compatible(np, type, compatible) \ + for (np = rt_ofw_find_node_by_compatible(RT_NULL, type, compatible); np; \ + np = rt_ofw_find_node_by_compatible(np, type, compatible)) + +#define rt_ofw_foreach_node_by_ids_r(np, id, ids) \ + for (np = rt_ofw_find_node_by_ids_r(RT_NULL, ids, id); \ + np; np = rt_ofw_find_node_by_ids_r(np, ids, id)) + +#define rt_ofw_foreach_node_by_ids(np, ids) \ + for (np = rt_ofw_find_node_by_ids(RT_NULL, ids); np; \ + np = rt_ofw_find_node_by_ids(np, ids)) + +#define rt_ofw_foreach_nodes(from, np) \ + for (np = rt_ofw_get_next_node(from); \ + np; np = rt_ofw_get_next_node(np)) + +#define rt_ofw_foreach_allnodes(np) \ + rt_ofw_foreach_nodes(RT_NULL, np) + +#define rt_ofw_foreach_parent_node(np) \ + for (np = rt_ofw_get_next_parent(rt_ofw_node_get(np)); \ + np; np = rt_ofw_get_next_parent(np)) + +#define rt_ofw_foreach_child_node(parent, child) \ + for (child = rt_ofw_get_next_child(parent, RT_NULL); \ + child; child = rt_ofw_get_next_child(parent, child)) + +#define rt_ofw_foreach_available_child_node(parent, child) \ + for (child = rt_ofw_get_next_available_child(parent, RT_NULL); child; \ + child = rt_ofw_get_next_available_child(parent, child)) + +#define rt_ofw_foreach_cpu_node(cpu_np) \ + for (cpu_np = rt_ofw_get_next_cpu_node(RT_NULL); \ + cpu_np; cpu_np = rt_ofw_get_next_cpu_node(cpu_np)) + +#define rt_ofw_foreach_prop(np, prop) \ + for (prop = np->props; prop; prop = prop->next) + +#define rt_ofw_foreach_prop_u32(np, propname, prop, p, u) \ + for (prop = rt_ofw_get_prop(np, propname, RT_NULL), \ + p = rt_ofw_prop_next_u32(prop, RT_NULL, &u); p; \ + p = rt_ofw_prop_next_u32(prop, p, &u)) + +#define rt_ofw_foreach_prop_string(np, propname, prop, s) \ + for (prop = rt_ofw_get_prop(np, propname, RT_NULL), \ + s = rt_ofw_prop_next_string(prop, RT_NULL); s; \ + s = rt_ofw_prop_next_string(prop, s)) + +#define rt_ofw_foreach_stub(stub, stub_start, stub_end) \ + for (stub = stub_start; stub <= stub_end; ++stub) + +struct rt_ofw_stub *rt_ofw_stub_probe_range(struct rt_ofw_node *np, + const struct rt_ofw_stub *stub_start, const struct rt_ofw_stub *stub_end); + +rt_err_t rt_ofw_console_setup(void); +const char *rt_ofw_bootargs_select(const char *key, int index); + +#ifdef RT_USING_CONSOLE +void rt_ofw_node_dump_dts(struct rt_ofw_node *np, rt_bool_t sibling_too); +#endif + +#endif /* __OFW_H__ */ diff --git a/components/drivers/include/drivers/ofw_fdt.h b/components/drivers/include/drivers/ofw_fdt.h new file mode 100755 index 0000000000..a9c951ca40 --- /dev/null +++ b/components/drivers/include/drivers/ofw_fdt.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-25 GuEe-GUI first version + */ + +#ifndef __OFW_FDT_H__ +#define __OFW_FDT_H__ + +#include +#include + +struct rt_fdt_earlycon +{ + union { rt_ubase_t mmio, port; }; + union { rt_ubase_t size, width; }; + + void *fdt; + long nodeoffset; + + void *data; + void (*console_putc)(void *data, char c); + +#define FDT_EARLYCON_KICK_UPDATE 0 +#define FDT_EARLYCON_KICK_COMPLETED 1 + void (*console_kick)(struct rt_fdt_earlycon *earlycon, int why); + + long msg_idx; + char msg[RT_FDT_EARLYCON_MSG_SIZE * 1024]; +}; + +struct rt_fdt_earlycon_id +{ + char *name; + char *type; + char *compatible; + rt_err_t (*setup)(struct rt_fdt_earlycon *earlycon, const char *options); +}; + +#define RT_FDT_EARLYCON_OPTION_SIGNATURE '\n' + +#define RT_FDT_EARLYCON_EXPORT(_name, _type, _compatible, _setup) \ +static const struct rt_fdt_earlycon_id __rt_fdt_##_name##_earlycon \ +rt_used RT_OFW_SYMBOL(earlycon, _) = \ +{ \ + .name = #_name, \ + .type = _type, \ + .compatible = _compatible, \ + .setup = _setup, \ +} + +const char *rt_fdt_node_name(const char *full_name); +rt_uint64_t rt_fdt_read_number(const fdt32_t *cell, int size); +rt_uint64_t rt_fdt_next_cell(const fdt32_t **cellptr, int size); +rt_uint64_t rt_fdt_translate_address(void *fdt, int nodeoffset, rt_uint64_t address); +rt_bool_t rt_fdt_device_is_available(void *fdt, int nodeoffset); + +rt_err_t rt_fdt_commit_memregion_early(rt_region_t *region, rt_bool_t is_reserved); +rt_err_t rt_fdt_commit_memregion_request(rt_region_t **out_region, rt_size_t *out_nr, rt_bool_t is_reserved); + +rt_err_t rt_fdt_prefetch(void *fdt); +rt_err_t rt_fdt_scan_root(void); +rt_err_t rt_fdt_scan_memory(void); +rt_err_t rt_fdt_scan_initrd(rt_uint64_t *ranges); +rt_err_t rt_fdt_model_dump(void); +rt_err_t rt_fdt_boot_dump(void); +void rt_fdt_earlycon_output(const char *str); +void rt_fdt_earlycon_kick(int why); +rt_err_t rt_fdt_scan_chosen_stdout(void); +rt_err_t rt_fdt_unflatten(void); + +struct rt_ofw_node *rt_fdt_unflatten_single(void *fdt); + +#endif /* __OFW_FDT_H__ */ diff --git a/components/drivers/include/drivers/ofw_io.h b/components/drivers/include/drivers/ofw_io.h new file mode 100755 index 0000000000..5fdfe8f5e6 --- /dev/null +++ b/components/drivers/include/drivers/ofw_io.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-25 GuEe-GUI first version + */ + +#ifndef __OFW_IO_H__ +#define __OFW_IO_H__ + +#include + +int rt_ofw_bus_addr_cells(struct rt_ofw_node *np); +int rt_ofw_bus_size_cells(struct rt_ofw_node *np); +int rt_ofw_io_addr_cells(struct rt_ofw_node *np); +int rt_ofw_io_size_cells(struct rt_ofw_node *np); + +int rt_ofw_get_address_count(struct rt_ofw_node *np); +rt_err_t rt_ofw_get_address(struct rt_ofw_node *np, int index, rt_uint64_t *out_address, rt_uint64_t *out_size); +rt_err_t rt_ofw_get_address_by_name(struct rt_ofw_node *np, const char *name, + rt_uint64_t *out_address, rt_uint64_t *out_size); +int rt_ofw_get_address_array(struct rt_ofw_node *np, int nr, rt_uint64_t *out_regs); + +rt_uint64_t rt_ofw_translate_address(struct rt_ofw_node *np, const char *range_type, rt_uint64_t address); + +void *rt_ofw_iomap(struct rt_ofw_node *np, int index); +void *rt_ofw_iomap_by_name(struct rt_ofw_node *np, const char *name); + +#endif /* __OFW_IO_H__ */ diff --git a/components/drivers/include/drivers/ofw_irq.h b/components/drivers/include/drivers/ofw_irq.h new file mode 100755 index 0000000000..e9b8482eac --- /dev/null +++ b/components/drivers/include/drivers/ofw_irq.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-25 GuEe-GUI first version + */ + +#ifndef __OFW_IRQ_H__ +#define __OFW_IRQ_H__ + +#include + +int rt_ofw_irq_cells(struct rt_ofw_node *np); + +rt_err_t rt_ofw_parse_irq_map(struct rt_ofw_node *np, struct rt_ofw_cell_args *irq_args); +rt_err_t rt_ofw_parse_irq_cells(struct rt_ofw_node *np, int index, struct rt_ofw_cell_args *out_irq_args); + +struct rt_ofw_node *rt_ofw_find_irq_parent(struct rt_ofw_node *np, int *out_interrupt_cells); +int rt_ofw_map_irq(struct rt_ofw_cell_args *irq_args); + +int rt_ofw_get_irq_count(struct rt_ofw_node *np); +int rt_ofw_get_irq(struct rt_ofw_node *np, int index); +int rt_ofw_get_irq_by_name(struct rt_ofw_node *np, const char *name); + +#endif /* __OFW_IRQ_H__ */ diff --git a/components/drivers/include/drivers/ofw_raw.h b/components/drivers/include/drivers/ofw_raw.h new file mode 100755 index 0000000000..13736c3010 --- /dev/null +++ b/components/drivers/include/drivers/ofw_raw.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-25 GuEe-GUI first version + */ + +#ifndef __OFW_RAW_H__ +#define __OFW_RAW_H__ + +#include + +#define FDT_SIZE_KB 1024 +#define FDT_SIZE_MB (1024 * FDT_SIZE_KB) + +#define FDT_SIZE_MAX (2 * FDT_SIZE_MB) +#define FDT_PADDING_SIZE (1 * FDT_SIZE_KB) + +typedef uint8_t fdt8_t; + +static inline uint8_t fdt8_to_cpu(fdt8_t x) +{ + return (uint8_t)x; +} + +int fdt_add_subnode_possible(void *fdt, int parentoffset, const char *name); +int fdt_add_mem_rsv_possible(void *fdt, size_t addr, size_t size); + +#define fdt_setprop_cstring(fdt, nodeoffset, name, str) \ + fdt_setprop((fdt), (nodeoffset), (name), (str), sizeof(str)) + +#define fdt_prop_cells_ops(ops, fdt, nodeoffset, prop, ...) \ +({ \ + int ret = 0; \ + uint32_t tmp[] = { __VA_ARGS__ }; \ + for (int i = 0; i < sizeof(tmp) / sizeof(tmp[0]); ++i) \ + { \ + tmp[i] = cpu_to_fdt32(tmp[i]); \ + } \ + ret += ops(fdt, nodeoffset, prop, tmp, sizeof(tmp)); \ + ret; \ +}) + +#define fdt_setprop_cells(fdt, nodeoffset, prop, ...) \ + fdt_prop_cells_ops(fdt_setprop, fdt, nodeoffset, prop, __VA_ARGS__) + +#define fdt_appendprop_cells(fdt, nodeoffset, prop, ...) \ + fdt_prop_cells_ops(fdt_appendprop, fdt, nodeoffset, prop, __VA_ARGS__) + +int fdt_setprop_uxx(void *fdt, int nodeoffset, const char *name, uint64_t val, bool is_u64); +int fdt_getprop_u8(void *fdt, int nodeoffset, const char *name, uint8_t *out_value, int *lenp); +int fdt_getprop_s8(void *fdt, int nodeoffset, const char *name, int8_t *out_value, int *lenp); +int fdt_getprop_u16(void *fdt, int nodeoffset, const char *name, uint16_t *out_value, int *lenp); +int fdt_getprop_s16(void *fdt, int nodeoffset, const char *name, int16_t *out_value, int *lenp); +int fdt_getprop_u32(void *fdt, int nodeoffset, const char *name, uint32_t *out_value, int *lenp); +int fdt_getprop_s32(void *fdt, int nodeoffset, const char *name, int32_t *out_value, int *lenp); + +int fdt_io_addr_cells(void *fdt, int nodeoffset); +int fdt_io_size_cells(void *fdt, int nodeoffset); + +int fdt_install_initrd(void *fdt, char *os_name, size_t initrd_addr, size_t initrd_size); + +#endif /* __OFW_RAW_H__ */ diff --git a/components/drivers/include/drivers/pic.h b/components/drivers/include/drivers/pic.h new file mode 100755 index 0000000000..5c6ac24c50 --- /dev/null +++ b/components/drivers/include/drivers/pic.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-24 GuEe-GUI first version + */ + +#ifndef __PIC_H__ +#define __PIC_H__ + +#include + +#include +#include +#include + +struct rt_pci_msi_desc; +struct rt_pci_msi_msg; + +struct rt_pic_ops; +struct rt_pic_irq; + +struct rt_pic +{ + rt_list_t list; + + struct rt_pic_ops *ops; + + void *priv_data; + void *user_data; + + struct rt_pic *parent; + + int irq_start; + rt_size_t irq_nr; + struct rt_pic_irq *pirqs; +}; + +struct rt_pic_ops +{ + const char *name; + + rt_err_t (*irq_init)(struct rt_pic *pic); + rt_err_t (*irq_finit)(struct rt_pic *pic); + + void (*irq_enable)(struct rt_pic_irq *pirq); + void (*irq_disable)(struct rt_pic_irq *pirq); + void (*irq_ack)(struct rt_pic_irq *pirq); + void (*irq_mask)(struct rt_pic_irq *pirq); + void (*irq_unmask)(struct rt_pic_irq *pirq); + void (*irq_eoi)(struct rt_pic_irq *pirq); + + rt_err_t (*irq_set_priority)(struct rt_pic_irq *pirq, rt_uint32_t priority); + rt_err_t (*irq_set_affinity)(struct rt_pic_irq *pirq, rt_bitmap_t *affinity); + rt_err_t (*irq_set_triger_mode)(struct rt_pic_irq *pirq, rt_uint32_t mode); + + void (*irq_send_ipi)(struct rt_pic_irq *pirq, rt_bitmap_t *cpumask); + + void (*irq_compose_msi_msg)(struct rt_pic_irq *pirq, struct rt_pci_msi_msg *msg); + void (*irq_write_msi_msg)(struct rt_pic_irq *pirq, struct rt_pci_msi_msg *msg); + int (*irq_alloc_msi)(struct rt_pic *pic, struct rt_pci_msi_desc *msi_desc); + void (*irq_free_msi)(struct rt_pic *pic, int irq); + + int (*irq_map)(struct rt_pic *pic, int hwirq, rt_uint32_t mode); + rt_err_t (*irq_parse)(struct rt_pic *pic, struct rt_ofw_cell_args *args, struct rt_pic_irq *out_pirq); +}; + +struct rt_pic_isr +{ + rt_list_t list; + +#define RT_IRQ_F_NONE 0 + int flags; + struct rt_irq_desc action; +}; + +struct rt_pic_irq +{ + int irq; + int hwirq; + +#define RT_IRQ_MODE_NONE 0 +#define RT_IRQ_MODE_EDGE_RISING 1 +#define RT_IRQ_MODE_EDGE_FALLING 2 +#define RT_IRQ_MODE_EDGE_BOTH (RT_IRQ_MODE_EDGE_FALLING | RT_IRQ_MODE_EDGE_RISING) +#define RT_IRQ_MODE_LEVEL_HIGH 4 +#define RT_IRQ_MODE_LEVEL_LOW 8 +#define RT_IRQ_MODE_LEVEL_MASK (RT_IRQ_MODE_LEVEL_LOW | RT_IRQ_MODE_LEVEL_HIGH) +#define RT_IRQ_MODE_MASK 0xf + rt_uint32_t mode; + + rt_uint32_t priority; + RT_DECLARE_BITMAP(affinity, RT_CPUS_NR); + + struct rt_pci_msi_desc *msi_desc; + + struct rt_pic_isr isr; + + struct rt_spinlock rw_lock; + + struct rt_pic *pic; +}; + +rt_err_t rt_pic_linear_irq(struct rt_pic *pic, rt_size_t irq_nr); + +int rt_pic_config_ipi(struct rt_pic *pic, int ipi_index, int hwirq); +int rt_pic_config_irq(struct rt_pic *pic, int irq_index, int hwirq); + +rt_inline struct rt_pic_irq *rt_pic_find_irq(struct rt_pic *pic, int irq_index) +{ + /* This is a quickly interface */ + RT_ASSERT(pic != RT_NULL); + RT_ASSERT(pic->pirqs != RT_NULL); + RT_ASSERT(irq_index < pic->irq_nr); + + return &pic->pirqs[irq_index]; +} + +struct rt_pic_irq *rt_pic_find_ipi(struct rt_pic *pic, int ipi_index); + +int rt_pic_cascade(struct rt_pic *pic, struct rt_pic *parent_pic, int hwirq, rt_uint32_t mode); +void rt_pic_uncascade(struct rt_pic *pic, int irq); + +rt_err_t rt_pic_attach_irq(int irq, rt_isr_handler_t handler, void *uid, const char *name, int flags); +rt_err_t rt_pic_detach_irq(int irq, void *uid); + +rt_err_t rt_pic_add_traps(rt_bool_t (*handler)(void *), void *data); +rt_err_t rt_pic_do_traps(void); +rt_err_t rt_pic_handle_isr(struct rt_pic_irq *pirq); + +/* User-implemented extensions */ +rt_err_t rt_pic_user_extends(struct rt_pic *pic); + +rt_err_t rt_pic_irq_init(void); +rt_err_t rt_pic_irq_finit(void); +void rt_pic_irq_enable(int irq); +void rt_pic_irq_disable(int irq); +void rt_pic_irq_ack(int irq); +void rt_pic_irq_mask(int irq); +void rt_pic_irq_unmask(int irq); +void rt_pic_irq_eoi(int irq); +rt_err_t rt_pic_irq_set_priority(int irq, rt_uint32_t priority); +rt_uint32_t rt_pic_irq_get_priority(int irq); +rt_err_t rt_pic_irq_set_affinity(int irq, rt_bitmap_t *affinity); +rt_err_t rt_pic_irq_get_affinity(int irq, rt_bitmap_t *out_affinity); +rt_err_t rt_pic_irq_set_triger_mode(int irq, rt_uint32_t mode); +rt_uint32_t rt_pic_irq_get_triger_mode(int irq); +void rt_pic_irq_send_ipi(int irq, rt_bitmap_t *cpumask); + +void rt_pic_irq_parent_enable(struct rt_pic *ppic, struct rt_pic_irq *pirq); +void rt_pic_irq_parent_disable(struct rt_pic *ppic, struct rt_pic_irq *pirq); +void rt_pic_irq_parent_ack(struct rt_pic *ppic, struct rt_pic_irq *pirq); +void rt_pic_irq_parent_mask(struct rt_pic *ppic, struct rt_pic_irq *pirq); +void rt_pic_irq_parent_unmask(struct rt_pic *ppic, struct rt_pic_irq *pirq); +void rt_pic_irq_parent_eoi(struct rt_pic *ppic, struct rt_pic_irq *pirq); +rt_err_t rt_pic_irq_parent_set_priority(struct rt_pic *ppic, struct rt_pic_irq *pirq, rt_uint32_t priority); +rt_err_t rt_pic_irq_parent_set_affinity(struct rt_pic *ppic, struct rt_pic_irq *pirq, rt_bitmap_t *affinity); +rt_err_t rt_pic_irq_parent_set_triger_mode(struct rt_pic *ppic, struct rt_pic_irq *pirq, rt_uint32_t mode); + +#define RT_PIC_OFW_DECLARE(name, ids, handler) RT_OFW_STUB_EXPORT(name, ids, pic, handler) + +rt_err_t rt_pic_init(void); + +#endif /* __PIC_H__ */ diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index 60da08f8b1..06e5c7158a 100644 --- a/components/drivers/include/rtdevice.h +++ b/components/drivers/include/rtdevice.h @@ -171,6 +171,18 @@ extern "C" { #ifdef RT_USING_DM #include "drivers/core/rtdm.h" + +#ifdef RT_USING_OFW +#include "drivers/ofw.h" +#include "drivers/ofw_fdt.h" +#include "drivers/ofw_io.h" +#include "drivers/ofw_irq.h" +#include "drivers/ofw_raw.h" +#endif /* RT_USING_OFW */ + +#ifdef RT_USING_PIC +#include "drivers/pic.h" +#endif #endif /* RT_USING_DM */ #ifdef __cplusplus diff --git a/components/drivers/ofw/Kconfig b/components/drivers/ofw/Kconfig new file mode 100755 index 0000000000..bebb5b4384 --- /dev/null +++ b/components/drivers/ofw/Kconfig @@ -0,0 +1,22 @@ +menuconfig RT_USING_OFW + bool "Using Open Firmware (OFW)" + select RT_USING_ADT + select RT_USING_ADT_REF + select RT_USING_ADT_BITMAP + depends on RT_USING_DM + default n + +config RT_USING_BUILTIN_FDT + bool "Using builtin fdt in kernel" + depends on RT_USING_OFW + default n + +config RT_BUILTIN_FDT_PATH + string "Builtin fdt path, will rebuild if have dts" + depends on RT_USING_BUILTIN_FDT + default "rtthread.dtb" + +config RT_FDT_EARLYCON_MSG_SIZE + int "Earlycon message buffer size (KB)" + depends on RT_USING_OFW + default 128 diff --git a/components/drivers/ofw/SConscript b/components/drivers/ofw/SConscript new file mode 100644 index 0000000000..d878c0c64a --- /dev/null +++ b/components/drivers/ofw/SConscript @@ -0,0 +1,22 @@ +from building import * + +objs = [] + +if not GetDepend(['RT_USING_OFW']): + Return('objs') + +cwd = GetCurrentDir() +list = os.listdir(cwd) +CPPPATH = [cwd, cwd + '/../include'] + +src = Glob('*.c') + +group = DefineGroup('DeviceDrivers', src, depend = [''], 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') diff --git a/components/drivers/ofw/base.c b/components/drivers/ofw/base.c new file mode 100644 index 0000000000..63f6d2702e --- /dev/null +++ b/components/drivers/ofw/base.c @@ -0,0 +1,1511 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-25 GuEe-GUI first version + */ + +#include + +#include +#include +#include +#include + +#define DBG_TAG "rtdm.ofw" +#define DBG_LVL DBG_INFO +#include + +#include "ofw_internal.h" + +struct rt_ofw_node *ofw_node_root = RT_NULL; +struct rt_ofw_node *ofw_node_cpus = RT_NULL; +struct rt_ofw_node *ofw_node_chosen = RT_NULL; +struct rt_ofw_node *ofw_node_aliases = RT_NULL; +struct rt_ofw_node *ofw_node_reserved_memory = RT_NULL; + +static rt_phandle _phandle_range[2] = { 1, 1 }; +static struct rt_ofw_node **_phandle_hash = RT_NULL; + +static rt_list_t _aliases_nodes = RT_LIST_OBJECT_INIT(_aliases_nodes); + +rt_err_t ofw_phandle_hash_reset(rt_phandle min, rt_phandle max) +{ + rt_err_t err = RT_EOK; + struct rt_ofw_node **hash_ptr = RT_NULL; + + max = RT_ALIGN(max, OFW_NODE_MIN_HASH); + + if (max > _phandle_range[1]) + { + rt_size_t size = sizeof(*_phandle_hash) * (max - min); + + if (!_phandle_hash) + { + hash_ptr = rt_calloc(1, size); + } + else + { + hash_ptr = rt_realloc(_phandle_hash, size); + + if (hash_ptr) + { + rt_size_t old_max = _phandle_range[1]; + + rt_memset(&hash_ptr[old_max], 0, sizeof(_phandle_hash) * (max - old_max)); + } + } + } + + if (hash_ptr) + { + /* We always reset min value only once */ + if (min) + { + _phandle_range[0] = min; + } + _phandle_range[1] = max; + + _phandle_hash = hash_ptr; + } + else + { + err = -RT_ENOMEM; + } + + return err; +} + +static void ofw_prop_destroy(struct rt_ofw_prop *prop) +{ + struct rt_ofw_prop *next; + + while (prop) + { + next = prop->next; + + rt_free(prop); + + prop = next; + } +} + +static struct rt_ofw_node *ofw_get_next_node(struct rt_ofw_node *prev) +{ + struct rt_ofw_node *np; + + /* + * Walk: + * + * / { ------------------------ [0] (START) has child, goto child. + * + * node0 { ---------------- [1] has child, goto child. + * + * node0_0 { ---------- [2] no child, has sibling, goto sibling. + * }; + * + * node0_1 { ---------- [3] no sibling now. + * upward while the parent has sibling. + * }; + * }; + * + * node1 { ---------------- [4] come from node0 who find the sibling: + * node1, node1 has child, goto child. + * + * node1_0 { ---------- [5] has child, goto child. + * + * node1_0_0 { ---- [6] no sibling now. + * upward while the parent has sibling. + * (END) in the root. + * }; + * }; + * }; + * }; + */ + + if (!prev) + { + np = ofw_node_root; + } + else if (prev->child) + { + np = prev->child; + } + else + { + np = prev; + + while (np->parent && !np->sibling) + { + np = np->parent; + } + + np = np->sibling; + } + + return np; +} + +static void ofw_node_destroy(struct rt_ofw_node *np) +{ + struct rt_ofw_node *prev; + + if (np->parent) + { + /* Ask parent and prev sibling we are destroy. */ + prev = np->parent->child; + + if (prev == np) + { + np->parent->child = RT_NULL; + } + else + { + while (prev->sibling != np) + { + prev = prev->sibling; + } + + prev->sibling = np->sibling; + } + } + + while (np) + { + if (rt_ofw_node_test_flag(np, RT_OFW_F_SYSTEM) == RT_FALSE) + { + LOG_E("%s is system node", np->full_name); + RT_ASSERT(0); + } + + prev = np; + + np = ofw_get_next_node(np); + + ofw_prop_destroy(prev->props); + rt_free(prev); + } +} + +rt_err_t rt_ofw_node_destroy(struct rt_ofw_node *np) +{ + rt_err_t err = RT_EOK; + + if (np) + { + if (rt_ref_read(&np->ref) <= 1) + { + ofw_node_destroy(np); + } + else + { + err = -RT_EBUSY; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +struct rt_ofw_node *rt_ofw_node_get(struct rt_ofw_node *np) +{ + if (np) + { + LOG_D("%s get ref = %d", np->full_name, rt_ref_read(&np->ref)); + rt_ref_get(&np->ref); + } + + return np; +} + +static void ofw_node_release(struct rt_ref *r) +{ + struct rt_ofw_node *np = rt_container_of(r, struct rt_ofw_node, ref); + + LOG_E("%s is release", np->full_name); + + RT_ASSERT(0); +} + +void rt_ofw_node_put(struct rt_ofw_node *np) +{ + if (np) + { + LOG_D("%s put ref = %d", np->full_name, rt_ref_read(&np->ref)); + rt_ref_put(&np->ref, &ofw_node_release); + } +} + +rt_bool_t rt_ofw_node_tag_equ(const struct rt_ofw_node *np, const char *tag) +{ + rt_bool_t ret = RT_FALSE; + + if (np && tag) + { + const char *node_name = rt_fdt_node_name(np->full_name); + rt_size_t tag_len = rt_strchrnul(node_name, '@') - node_name; + + ret = (rt_strlen(tag) == tag_len && !rt_strncmp(node_name, tag, tag_len)); + } + + return ret; +} + +rt_bool_t rt_ofw_node_tag_prefix(const struct rt_ofw_node *np, const char *prefix) +{ + rt_bool_t ret = RT_FALSE; + + if (np && prefix) + { + ret = !rt_strncmp(rt_fdt_node_name(np->full_name), prefix, rt_strlen(prefix)); + } + + return ret; +} + +static int ofw_prop_index_of_string(struct rt_ofw_prop *prop, const char *string, + rt_int32_t (*cmp)(const char *cs, const char *ct)) +{ + int index = -1; + rt_size_t len = prop->length, slen = 0; + const char *value = prop->value; + + for (int idx = 0; len > 0; ++idx) + { + /* Add '\0' */ + slen = rt_strlen(value) + 1; + + if (!cmp(value, string)) + { + index = idx; + + break; + } + + len -= slen; + value += slen; + } + + return index; +} + +static rt_int32_t ofw_strcasecmp(const char *cs, const char *ct) +{ + extern rt_int32_t strcasecmp(const char *cs, const char *ct); + + return rt_strcasecmp(cs, ct); +} + +static int ofw_prop_index_of_compatible(struct rt_ofw_prop *prop, const char *compatible) +{ + return ofw_prop_index_of_string(prop, compatible, ofw_strcasecmp); +} + +static int ofw_node_index_of_compatible(const struct rt_ofw_node *np, const char *compatible) +{ + int idx = -1; + struct rt_ofw_prop *prop = rt_ofw_get_prop(np, "compatible", RT_NULL); + + if (prop) + { + idx = ofw_prop_index_of_compatible(prop, compatible); + } + + return idx; +} + +rt_bool_t rt_ofw_machine_is_compatible(const char *compatible) +{ + return ofw_node_index_of_compatible(ofw_node_root, compatible) >= 0; +} + +/* + * Property status: + * + * "okay" or "ok": + * Indicates the device is operational. + * + * "disabled": + * Indicates that the device is not presently operational, but it might + * become operational in the future (for example, something is not + * plugged in, or switched off). + * Refer to the device binding for details on what disabled means for a + * given device. + * + * "reserved": + * Indicates that the device is operational, but should not be used. + * Typically this is used for devices that are controlled by another + * software component, such as platform firmware. + * + * "fail": + * Indicates that the device is not operational. A serious error was + * detected in the device, and it is unlikely to become operational + * without repair. + * + * "fail-sss": + * Indicates that the device is not operational. A serious error was + * detected in the device and it is unlikely to become operational + * without repair. The sss portion of the value is specific to the + * device and indicates the error condition detected. + */ + +static rt_bool_t ofw_node_is_fail(const struct rt_ofw_node *np) +{ + rt_bool_t res = RT_FALSE; + const char *status = rt_ofw_prop_read_raw(np, "status", RT_NULL); + + if (status) + { + res = !rt_strcmp(status, "fail") || !rt_strncmp(status, "fail-", 5); + } + + return res; +} + +static rt_bool_t ofw_node_is_available(const struct rt_ofw_node *np) +{ + rt_bool_t res = RT_TRUE; + const char *status = rt_ofw_prop_read_raw(np, "status", RT_NULL); + + if (status) + { + res = !rt_strcmp(status, "okay") || !rt_strcmp(status, "ok"); + } + + return res; +} + +rt_bool_t rt_ofw_node_is_available(const struct rt_ofw_node *np) +{ + return np ? ofw_node_is_available(np) : RT_FALSE; +} + +rt_bool_t rt_ofw_node_is_compatible(const struct rt_ofw_node *np, const char *compatible) +{ + rt_bool_t res = RT_FALSE; + + if (np) + { + res = ofw_node_index_of_compatible(np, compatible) >= 0; + } + + return res; +} + +static struct rt_ofw_node_id *ofw_prop_match(struct rt_ofw_prop *prop, const struct rt_ofw_node_id *ids) +{ + int best_index = RT_UINT32_MAX >> 1, index; + struct rt_ofw_node_id *found_id = RT_NULL, *id; + + for (id = (struct rt_ofw_node_id *)ids; id->compatible[0]; ++id) + { + index = ofw_prop_index_of_compatible(prop, id->compatible); + + if (index >= 0 && index < best_index) + { + found_id = id; + best_index = index; + } + } + + return found_id; +} + +struct rt_ofw_node_id *rt_ofw_prop_match(struct rt_ofw_prop *prop, const struct rt_ofw_node_id *ids) +{ + struct rt_ofw_node_id *id = RT_NULL; + + if (prop && ids && !rt_strcmp(prop->name, "compatible")) + { + id = ofw_prop_match(prop, ids); + } + + return id; +} + +struct rt_ofw_node_id *rt_ofw_node_match(struct rt_ofw_node *np, const struct rt_ofw_node_id *ids) +{ + struct rt_ofw_prop *prop; + struct rt_ofw_node_id *id = RT_NULL; + + if (np && ids && (prop = rt_ofw_get_prop(np, "compatible", RT_NULL))) + { + id = ofw_prop_match(prop, ids); + } + + return id; +} + +struct rt_ofw_node *rt_ofw_find_node_by_tag(struct rt_ofw_node *from, const char *tag) +{ + struct rt_ofw_node *np = RT_NULL; + + if (tag) + { + rt_ofw_foreach_nodes(from, np) + { + if (rt_ofw_node_tag_equ(np, tag)) + { + break; + } + } + } + + return np; +} + +struct rt_ofw_node *rt_ofw_find_node_by_prop_r(struct rt_ofw_node *from, const char *propname, + const struct rt_ofw_prop **out_prop) +{ + struct rt_ofw_node *np = RT_NULL; + + if (propname) + { + rt_ofw_foreach_nodes(from, np) + { + struct rt_ofw_prop *prop = rt_ofw_get_prop(np, propname, RT_NULL); + + if (prop) + { + if (out_prop) + { + *out_prop = prop; + } + + break; + } + } + } + + return np; +} + +struct rt_ofw_node *rt_ofw_find_node_by_name(struct rt_ofw_node *from, const char *name) +{ + struct rt_ofw_node *np = RT_NULL; + + if (name) + { + rt_ofw_foreach_nodes(from, np) + { + if (np->name && !rt_strcmp(np->name, name)) + { + np = rt_ofw_node_get(np); + break; + } + } + } + + return np; +} + +struct rt_ofw_node *rt_ofw_find_node_by_type(struct rt_ofw_node *from, const char *type) +{ + struct rt_ofw_node *np = RT_NULL; + + if (type) + { + rt_ofw_foreach_nodes(from, np) + { + if (rt_ofw_node_is_type(np, type)) + { + break; + } + } + } + + return np; +} + +struct rt_ofw_node *rt_ofw_find_node_by_compatible(struct rt_ofw_node *from, const char *compatible) +{ + struct rt_ofw_node *np = RT_NULL; + + if (compatible) + { + rt_ofw_foreach_nodes(from, np) + { + if (ofw_node_index_of_compatible(np, compatible) >= 0) + { + break; + } + } + } + + return np; +} + +struct rt_ofw_node *rt_ofw_find_node_by_ids_r(struct rt_ofw_node *from, const struct rt_ofw_node_id *ids, + const struct rt_ofw_node_id **out_id) +{ + struct rt_ofw_node *np = RT_NULL; + + if (ids) + { + rt_ofw_foreach_nodes(from, np) + { + struct rt_ofw_node_id *id = rt_ofw_node_match(np, ids); + + if (id) + { + if (out_id) + { + *out_id = id; + } + + break; + } + } + } + + return np; +} + +struct rt_ofw_node *rt_ofw_find_node_by_path(const char *path) +{ + struct rt_ofw_node *np, *parent, *tmp; + + if (path) + { + if (!rt_strcmp(path, "/")) + { + np = ofw_node_root; + } + else + { + ++path; + parent = rt_ofw_node_get(ofw_node_root); + + while (*path) + { + const char *next = rt_strchrnul(path, '/'); + rt_size_t len = next - path; + + tmp = RT_NULL; + + rt_ofw_foreach_child_node(parent, np) + { + if (!rt_strncmp(np->full_name, path, len)) + { + rt_ofw_node_put(parent); + + parent = np; + tmp = np; + + break; + } + } + + if (!tmp) + { + rt_ofw_node_put(parent); + + break; + } + + path += len; + } + + np = tmp; + } + + rt_ofw_node_get(np); + } + + return np; +} + +struct rt_ofw_node *rt_ofw_find_node_by_phandle(rt_phandle phandle) +{ + struct rt_ofw_node *np = RT_NULL; + + if (phandle >= OFW_PHANDLE_MIN && phandle <= OFW_PHANDLE_MAX) + { + /* rebase from zero */ + rt_phandle poff = phandle - _phandle_range[0]; + + np = _phandle_hash[poff]; + + if (!np) + { + rt_ofw_foreach_allnodes(np) + { + if (np->phandle == phandle) + { + _phandle_hash[poff] = np; + + break; + } + } + } + else + { + rt_ofw_node_get(np); + } + } + + return np; +} + +struct rt_ofw_node *rt_ofw_get_parent(const struct rt_ofw_node *np) +{ + if (np) + { + np = rt_ofw_node_get(np->parent); + } + + return (struct rt_ofw_node *)np; +} + +struct rt_ofw_node *rt_ofw_get_child_by_tag(const struct rt_ofw_node *parent, const char *tag) +{ + struct rt_ofw_node *child = RT_NULL; + + if (parent && tag) + { + rt_ofw_foreach_child_node(parent, child) + { + if (rt_ofw_node_tag_equ(child, tag)) + { + break; + } + } + } + + return child; +} + +struct rt_ofw_node *rt_ofw_get_child_by_compatible(const struct rt_ofw_node *parent, const char *compatible) +{ + struct rt_ofw_node *child = RT_NULL; + + if (parent && compatible) + { + rt_ofw_foreach_child_node(parent, child) + { + if (ofw_node_index_of_compatible(child, compatible) >= 0) + { + break; + } + } + } + + return child; +} + +int rt_ofw_get_child_count(const struct rt_ofw_node *np) +{ + int nr; + + if (np) + { + struct rt_ofw_node *child; + + nr = 0; + + rt_ofw_foreach_child_node(np, child) + { + ++nr; + } + } + else + { + nr = -RT_EINVAL; + } + + return nr; +} + +int rt_ofw_get_available_child_count(const struct rt_ofw_node *np) +{ + int nr; + + if (np) + { + struct rt_ofw_node *child; + + nr = 0; + + rt_ofw_foreach_available_child_node(np, child) + { + ++nr; + } + } + else + { + nr = -RT_EINVAL; + } + + return nr; +} + +struct rt_ofw_node *rt_ofw_get_next_node(struct rt_ofw_node *prev) +{ + struct rt_ofw_node *np; + + np = rt_ofw_node_get(ofw_get_next_node(prev)); + rt_ofw_node_put(prev); + + return np; +} + +struct rt_ofw_node *rt_ofw_get_next_parent(struct rt_ofw_node *prev) +{ + struct rt_ofw_node *next = RT_NULL; + + if (prev) + { + next = rt_ofw_node_get(prev->parent); + rt_ofw_node_put(prev); + } + + return next; +} + +struct rt_ofw_node *rt_ofw_get_next_child(const struct rt_ofw_node *parent, struct rt_ofw_node *prev) +{ + struct rt_ofw_node *next = RT_NULL; + + if (parent) + { + next = prev ? prev->sibling : parent->child; + rt_ofw_node_put(prev); + rt_ofw_node_get(next); + } + + return next; +} + +struct rt_ofw_node *rt_ofw_get_next_available_child(const struct rt_ofw_node *parent, struct rt_ofw_node *prev) +{ + struct rt_ofw_node *next = RT_NULL; + + if (parent) + { + next = prev; + + do { + next = rt_ofw_get_next_child(parent, next); + + } while (next && !ofw_node_is_available(next)); + } + + return next; +} + +struct rt_ofw_node *rt_ofw_get_cpu_node(int cpu, int *thread, rt_bool_t (*match_cpu_hwid)(int cpu, rt_uint64_t hwid)) +{ + const char *propname = "reg"; + struct rt_ofw_node *cpu_np = RT_NULL; + + /* + * "reg" (some of the obsolete arch may be other names): + * The value of reg is a that defines a unique + * CPU/thread id for the CPU/threads represented by the CPU node. + * + * If a CPU supports more than one thread (i.e. multiple streams of + * execution) the reg property is an array with 1 element per thread. The + * #address-cells on the /cpus node specifies how many cells each element + * of the array takes. Software can determine the number of threads by + * dividing the size of reg by the parent node’s #address-cells: + * + * thread-number = reg-cells / address-cells + * + * If a CPU/thread can be the target of an external interrupt the reg + * property value must be a unique CPU/thread id that is addressable by the + * interrupt controller. + * + * If a CPU/thread cannot be the target of an external interrupt, then reg + * must be unique and out of bounds of the range addressed by the interrupt + * controller + * + * If a CPU/thread’s PIR (pending interrupt register) is modifiable, a + * client program should modify PIR to match the reg property value. If PIR + * cannot be modified and the PIR value is distinct from the interrupt + * controller number space, the CPUs binding may define a binding-specific + * representation of PIR values if desired. + */ + + rt_ofw_foreach_cpu_node(cpu_np) + { + rt_ssize_t prop_len; + rt_bool_t is_end = RT_FALSE; + int tid, addr_cells = rt_ofw_io_addr_cells(cpu_np); + const fdt32_t *cell = rt_ofw_prop_read_raw(cpu_np, propname, &prop_len); + + if (!cell && !addr_cells) + { + if (match_cpu_hwid && match_cpu_hwid(cpu, 0)) + { + break; + } + + continue; + } + + if (!match_cpu_hwid) + { + continue; + } + + prop_len /= sizeof(*cell) * addr_cells; + + for (tid = 0; tid < prop_len; ++tid) + { + rt_uint64_t hwid = rt_fdt_read_number(cell, addr_cells); + + if (match_cpu_hwid(cpu, hwid)) + { + if (thread) + { + *thread = tid; + } + + is_end = RT_TRUE; + + break; + } + + cell += addr_cells; + } + + if (is_end) + { + break; + } + } + + return cpu_np; +} + +struct rt_ofw_node *rt_ofw_get_next_cpu_node(struct rt_ofw_node *prev) +{ + struct rt_ofw_node *cpu_np; + + if (prev) + { + cpu_np = prev->sibling; + rt_ofw_node_put(prev); + } + else + { + cpu_np = ofw_node_cpus->child; + } + + for (; cpu_np; cpu_np = cpu_np->sibling) + { + if (ofw_node_is_fail(cpu_np)) + { + continue; + } + + if (!(rt_ofw_node_tag_equ(cpu_np, "cpu") || rt_ofw_node_is_type(cpu_np, "cpu"))) + { + continue; + } + + if (rt_ofw_node_get(cpu_np)) + { + break; + } + } + + return cpu_np; +} + +struct rt_ofw_node *rt_ofw_get_cpu_state_node(struct rt_ofw_node *cpu_np, int index) +{ + struct rt_ofw_cell_args args; + struct rt_ofw_node *np = RT_NULL, *state_np; + rt_err_t err = rt_ofw_parse_phandle_cells(cpu_np, "power-domains", "#power-domain-cells", 0, &args); + + if (!err) + { + state_np = rt_ofw_parse_phandle(args.data, "domain-idle-states", index); + + rt_ofw_node_put(args.data); + + if (state_np) + { + np = state_np; + } + } + + if (!np) + { + int count = 0; + rt_uint32_t phandle; + const fdt32_t *cell; + struct rt_ofw_prop *prop; + + rt_ofw_foreach_prop_u32(cpu_np, "cpu-idle-states", prop, cell, phandle) + { + if (count == index) + { + np = rt_ofw_find_node_by_phandle((rt_phandle)phandle); + + break; + } + + ++count; + } + } + + return np; +} + +rt_uint64_t rt_ofw_get_cpu_id(struct rt_ofw_node *cpu_np) +{ + rt_uint64_t cpuid = ~0ULL; + + if (cpu_np) + { + rt_uint64_t idx = 0; + struct rt_ofw_node *np = ofw_node_cpus->child; + + for (; np; np = np->sibling) + { + if (!(rt_ofw_node_tag_equ(cpu_np, "cpu") || rt_ofw_node_is_type(cpu_np, "cpu"))) + { + continue; + } + + if (cpu_np == np) + { + cpuid = idx; + + break; + } + + ++idx; + } + + if ((rt_int64_t)cpuid < 0 && !rt_ofw_prop_read_u64(cpu_np, "rt-thread,cpuid", &idx)) + { + cpuid = idx; + } + } + + return cpuid; +} + +rt_uint64_t rt_ofw_get_cpu_hwid(struct rt_ofw_node *cpu_np, unsigned int thread) +{ + rt_uint64_t thread_id, hwid = ~0ULL; + + if (cpu_np && thread >= 0 && !rt_ofw_get_address(cpu_np, thread, &thread_id, RT_NULL)) + { + hwid = thread_id; + } + + return hwid; +} + +rt_err_t ofw_alias_scan(void) +{ + rt_err_t err = RT_EOK; + struct rt_ofw_prop *prop; + struct rt_ofw_node *np = ofw_node_aliases, *tmp; + + rt_ofw_foreach_prop(np, prop) + { + int id = 0, rate = 1; + struct alias_info *info; + const char *name = prop->name, *end; + + /* Maybe the bootloader will set the name, or other nodes reference the aliases */ + if (!rt_strcmp(name, "name") || !rt_strcmp(name, "phandle")) + { + continue; + } + + if (!(tmp = rt_ofw_find_node_by_path(prop->value))) + { + continue; + } + + end = name + rt_strlen(name); + + while (*end && !(*end >= '0' && *end <= '9')) + { + --end; + } + + while (*end && (*end >= '0' && *end <= '9')) + { + id += (*end - '0') * rate; + rate *= 10; + + --end; + } + + info = rt_malloc(sizeof(*info)); + + if (!info) + { + err = -RT_ENOMEM; + break; + } + + rt_list_init(&info->list); + + info->id = id; + info->tag = name; + info->tag_len = end - name; + info->np = tmp; + + rt_list_insert_after(&_aliases_nodes, &info->list); + } + + return err; +} + +struct rt_ofw_node *rt_ofw_get_alias_node(const char *tag, int id) +{ + struct alias_info *info; + struct rt_ofw_node *np = RT_NULL; + + if (tag && id >= 0) + { + rt_list_for_each_entry(info, &_aliases_nodes, list) + { + if (rt_strncmp(info->tag, tag, info->tag_len)) + { + continue; + } + + if (info->id == id) + { + np = info->np; + break; + } + } + } + + return np; +} + +int rt_ofw_get_alias_id(struct rt_ofw_node *np, const char *tag) +{ + int id; + struct alias_info *info; + + if (np && tag) + { + id = -1; + + rt_list_for_each_entry(info, &_aliases_nodes, list) + { + if (rt_strncmp(info->tag, tag, info->tag_len)) + { + continue; + } + + if (info->np == np) + { + id = info->id; + break; + } + } + } + else + { + id = -RT_EINVAL; + } + + return id; +} + +int rt_ofw_get_alias_last_id(const char *tag) +{ + int id; + struct alias_info *info; + + if (tag) + { + id = -1; + + rt_list_for_each_entry(info, &_aliases_nodes, list) + { + if (rt_strncmp(info->tag, tag, info->tag_len)) + { + continue; + } + + if (info->id > id) + { + id = info->id; + } + } + } + else + { + id = -RT_EINVAL; + } + + return id; +} + +struct rt_ofw_node *rt_ofw_parse_phandle(const struct rt_ofw_node *np, const char *phandle_name, int index) +{ + struct rt_ofw_cell_args args; + struct rt_ofw_node *ref_np = RT_NULL; + + if (!rt_ofw_parse_phandle_cells(np, phandle_name, RT_NULL, index, &args)) + { + ref_np = args.data; + } + + return ref_np; +} + +static rt_err_t ofw_parse_phandle_cells(const struct rt_ofw_node *np, const char *list_name, const char *cells_name, + int index, struct rt_ofw_cell_args *out_args) +{ + rt_err_t err = -RT_EEMPTY; + rt_uint32_t value; + rt_size_t count = 0; + const fdt32_t *cell; + struct rt_ofw_prop *prop; + + /* + * List: + * + * phandle1: node1 { + * #list-cells = <2>; + * }; + * + * phandle2: node2 { + * #list-cells = <1>; + * }; + * + * node3 { + * list = <&phandle1 0xaa 0xbb>, <&phandle2 0xcc>; + * }; + * + * if call: + * rt_ofw_parse_phandle_cells(node3, "list", "#list-cells", 0, &args): + * + * args.data = node1; + * args.args_count = 2; + * args.args[0] = 0xaa; + * args.args[1] = 0xbb; + * + * rt_ofw_parse_phandle_cells(node3, "list", "#list-cells", 1, &args): + * + * args.data = node2; + * args.args_count = 1; + * args.args[0] = 0xcc; + */ + + rt_ofw_foreach_prop_u32(np, list_name, prop, cell, value) + { + rt_uint32_t cells_count = 0; + struct rt_ofw_node *phandle_np = rt_ofw_find_node_by_phandle((rt_phandle)value); + + /* if phandle node is undefined, we assume that the cels_count is 0 */ + if (cells_name && phandle_np) + { + rt_ofw_prop_read_u32(phandle_np, cells_name, &cells_count); + } + + if (count++ == index) + { + for (int idx = 0; idx < cells_count; ++idx) + { + cell = rt_ofw_prop_next_u32(prop, cell, &value); + + out_args->args[idx] = value; + } + + out_args->args_count = cells_count; + out_args->data = phandle_np; + + if (out_args->data) + { + err = RT_EOK; + } + + break; + } + + cell += cells_count; + } + + return err; +} + +rt_err_t rt_ofw_parse_phandle_cells(const struct rt_ofw_node *np, const char *list_name, const char *cells_name, + int index, struct rt_ofw_cell_args *out_args) +{ + rt_err_t err; + + if (np && list_name && index >= 0 && out_args) + { + err = ofw_parse_phandle_cells(np, list_name, cells_name, index, out_args); + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +int rt_ofw_count_phandle_cells(const struct rt_ofw_node *np, const char *list_name, const char *cells_name) +{ + int count; + + if (np && list_name) + { + count = -1; + + if (!cells_name) + { + rt_ssize_t length; + + if (rt_ofw_get_prop(np, list_name, &length)) + { + count = length / sizeof(fdt32_t); + } + } + else + { + int index = count = 0; + struct rt_ofw_cell_args args; + + while (!ofw_parse_phandle_cells(np, list_name, cells_name, index, &args)) + { + ++index; + ++count; + } + } + } + else + { + count = -RT_EINVAL; + } + + return count; +} + +struct rt_ofw_prop *rt_ofw_get_prop(const struct rt_ofw_node *np, const char *name, rt_ssize_t *out_length) +{ + struct rt_ofw_prop *prop = RT_NULL; + + if (np && name) + { + rt_ofw_foreach_prop(np, prop) + { + if (!rt_strcmp(prop->name, name)) + { + if (out_length) + { + *out_length = prop->length; + } + + break; + } + } + } + + return prop; +} + +#define OFW_PROP_READ_UXX_ARRAY_INDEX(bit) \ +int rt_ofw_prop_read_u##bit##_array_index( \ + const struct rt_ofw_node *np, const char *propname, \ + int index, int nr, rt_uint##bit##_t *out_values) \ +{ \ + int res, max_nr; \ + if (np && propname && index >= 0 && nr >= 0 && out_values) \ + { \ + rt_ssize_t len; \ + const fdt##bit##_t *elm; \ + elm = rt_ofw_prop_read_raw(np, propname, &len); \ + max_nr = len / sizeof(*elm); \ + if (elm && index < max_nr) \ + { \ + elm += index; \ + max_nr -= index; \ + res = nr > max_nr ? max_nr : nr; \ + for (nr = 0; nr < res; ++nr) \ + { \ + *out_values++ = fdt##bit##_to_cpu(*elm++); \ + } \ + } \ + else \ + { \ + res = -RT_EEMPTY; \ + } \ + } \ + else \ + { \ + res = -RT_EINVAL; \ + } \ + return res; \ +} + +OFW_PROP_READ_UXX_ARRAY_INDEX(8) +OFW_PROP_READ_UXX_ARRAY_INDEX(16) +OFW_PROP_READ_UXX_ARRAY_INDEX(32) +OFW_PROP_READ_UXX_ARRAY_INDEX(64) + +#undef OFW_PROP_READ_UXX_ARRAY_INDEX + +int rt_ofw_prop_read_string_array_index(const struct rt_ofw_node *np, const char *propname, + int index, int nr, const char **out_strings) +{ + int res = 0; + + if (np && propname && index >= 0 && nr >= 0 && out_strings) + { + rt_ssize_t len, slen = 0; + const char *value = rt_ofw_prop_read_raw(np, propname, &len); + + if (value) + { + nr += index; + + for (int idx = 0; idx < nr && len > 0; ++idx) + { + /* Add '\0' */ + slen = rt_strlen(value) + 1; + + if (idx >= index) + { + *out_strings++ = value; + + ++res; + } + + len -= slen; + value += slen; + } + } + else + { + res = -RT_EEMPTY; + } + } + else + { + res = -RT_EINVAL; + } + + return res; +} + +int rt_ofw_prop_count_of_size(const struct rt_ofw_node *np, const char *propname, int size) +{ + int count; + + if (np && propname && size > 0) + { + rt_ssize_t len; + + count = -RT_EEMPTY; + + if (rt_ofw_get_prop(np, propname, &len)) + { + count = len / size; + } + } + else + { + count = -RT_EINVAL; + } + + return count; +} + +static rt_int32_t ofw_strcmp(const char *cs, const char *ct) +{ + return rt_strcmp(cs, ct); +} + +int rt_ofw_prop_index_of_string(const struct rt_ofw_node *np, const char *propname, const char *string) +{ + int idx; + + if (np && propname && string) + { + struct rt_ofw_prop *prop = rt_ofw_get_prop(np, propname, RT_NULL); + + idx = -1; + + if (prop) + { + idx = ofw_prop_index_of_string(prop, string, ofw_strcmp); + } + } + else + { + idx = -RT_EINVAL; + } + + return idx; +} + +const fdt32_t *rt_ofw_prop_next_u32(struct rt_ofw_prop *prop, const fdt32_t *cur, rt_uint32_t *out_value) +{ + if (prop && out_value) + { + if (cur) + { + ++cur; + + if ((void *)cur >= prop->value + prop->length) + { + cur = RT_NULL; + } + } + else + { + cur = prop->value; + } + + if (cur) + { + *out_value = fdt32_to_cpu(*cur); + } + } + else + { + cur = RT_NULL; + } + + return cur; +} + +const char *rt_ofw_prop_next_string(struct rt_ofw_prop *prop, const char *cur) +{ + if (prop) + { + if (cur) + { + cur += rt_strlen(cur) + 1; + + if ((void *)cur >= prop->value + prop->length) + { + cur = RT_NULL; + } + } + else + { + cur = prop->value; + } + } + else + { + cur = RT_NULL; + } + + return cur; +} diff --git a/components/drivers/ofw/fdt.c b/components/drivers/ofw/fdt.c new file mode 100755 index 0000000000..69eb332bee --- /dev/null +++ b/components/drivers/ofw/fdt.c @@ -0,0 +1,1108 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-25 GuEe-GUI first version + */ + +#include +#include + +#include +#include +#include + +#define DBG_TAG "rtdm.ofw" +#define DBG_LVL DBG_INFO +#include + +#include "ofw_internal.h" + +struct rt_fdt_earlycon fdt_earlycon rt_section(".bss.noclean.earlycon"); + +RT_OFW_SYMBOL_TYPE_RANGE(earlycon, struct rt_fdt_earlycon_id, _earlycon_start = {}, _earlycon_end = {}); + +#ifndef ARCH_INIT_MEMREGION_NR +#define ARCH_INIT_MEMREGION_NR 128 +#endif + +static rt_region_t _memregion[ARCH_INIT_MEMREGION_NR] rt_section(".bss.noclean.memregion"); +static int _memregion_front_idx = 0, _memregion_last_idx = RT_ARRAY_SIZE(_memregion) - 1; + +static void *_fdt = RT_NULL; +static rt_phandle _phandle_min; +static rt_phandle _phandle_max; +static rt_size_t _root_size_cells; +static rt_size_t _root_addr_cells; + +const char *rt_fdt_node_name(const char *full_name) +{ + const char *node_name = rt_strrchr(full_name, '/'); + + return node_name ? node_name + 1 : full_name; +} + +rt_uint64_t rt_fdt_read_number(const fdt32_t *cell, int size) +{ + rt_uint64_t val = 0; + + for (; size--; ++cell) + { + val = (val << 32) | fdt32_to_cpu(*cell); + } + + return val; +} + +rt_uint64_t rt_fdt_next_cell(const fdt32_t **cellptr, int size) +{ + const fdt32_t *ptr = *cellptr; + + *cellptr = ptr + size; + + return rt_fdt_read_number(ptr, size); +} + +rt_uint64_t rt_fdt_translate_address(void *fdt, int nodeoffset, rt_uint64_t address) +{ + rt_uint64_t ret = address; + + if (fdt && nodeoffset >= 0) + { + struct + { + rt_uint64_t addr; + rt_size_t size; + int addr_cells; + int size_cells; + } local, cpu; + int parent, length, group_len; + const fdt32_t *ranges = RT_NULL; + + parent = fdt_parent_offset(fdt, nodeoffset); + + if (parent >= 0) + { + length = 0; + ranges = fdt_getprop(fdt, nodeoffset, "ranges", &length); + } + + if (ranges && length > 0) + { + local.addr_cells = fdt_address_cells(fdt, nodeoffset); + local.size_cells = fdt_size_cells(fdt, nodeoffset); + cpu.addr_cells = fdt_io_addr_cells(fdt, nodeoffset); + cpu.size_cells = fdt_io_size_cells(fdt, nodeoffset); + + group_len = local.addr_cells + cpu.addr_cells + local.size_cells; + + while (length > 0) + { + local.addr = rt_fdt_next_cell(&ranges, local.addr_cells); + cpu.addr = rt_fdt_next_cell(&ranges, cpu.addr_cells); + local.size = rt_fdt_next_cell(&ranges, local.size_cells); + + if (local.addr <= address && local.addr + local.size > address) + { + ret += address - cpu.addr; + break; + } + + length -= group_len; + } + } + } + + return ret; +} + +rt_bool_t rt_fdt_device_is_available(void *fdt, int nodeoffset) +{ + rt_bool_t ret; + + const char *status = fdt_getprop(fdt, nodeoffset, "status", RT_NULL); + + if (!status) + { + ret = RT_TRUE; + } + else if (!rt_strcmp(status, "ok") || !rt_strcmp(status, "okay")) + { + ret = RT_TRUE; + } + else + { + ret = RT_FALSE; + } + + return ret; +} + +rt_err_t rt_fdt_commit_memregion_early(rt_region_t *region, rt_bool_t is_reserved) +{ + rt_err_t err = RT_EOK; + + if (region && region->name) + { + if (_memregion_front_idx < _memregion_last_idx) + { + int idx; + + if (!_memregion_front_idx && _memregion_last_idx == RT_ARRAY_SIZE(_memregion) - 1) + { + for (int i = 0; i < RT_ARRAY_SIZE(_memregion); ++i) + { + _memregion[i].name = RT_NULL; + } + } + + idx = is_reserved ? _memregion_last_idx-- : _memregion_front_idx++; + + rt_memcpy(&_memregion[idx], region, sizeof(*region)); + } + else + { + err = -RT_EEMPTY; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_fdt_commit_memregion_request(rt_region_t **out_region, rt_size_t *out_nr, rt_bool_t is_reserved) +{ + rt_err_t err = RT_EOK; + + if (out_region && out_nr) + { + if (is_reserved) + { + *out_region = &_memregion[_memregion_last_idx + 1]; + *out_nr = RT_ARRAY_SIZE(_memregion) - 1 - _memregion_last_idx; + } + else + { + *out_region = &_memregion[0]; + *out_nr = _memregion_front_idx; + } + + if (*out_nr == 0) + { + err = -RT_EEMPTY; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_fdt_prefetch(void *fdt) +{ + rt_err_t err = -RT_ERROR; + + if (fdt) + { + _fdt = fdt; + + if (!fdt_check_header(_fdt)) + { + err = rt_fdt_scan_root(); + } + else + { + err = -RT_EINVAL; + } + } + + return err; +} + +rt_err_t rt_fdt_scan_root(void) +{ + rt_err_t err = RT_EOK; + int root = fdt_path_offset(_fdt, "/"); + + if (root >= 0) + { + const fdt32_t *prop; + + _root_addr_cells = OFW_ROOT_NODE_ADDR_CELLS_DEFAULT; + _root_size_cells = OFW_ROOT_NODE_SIZE_CELLS_DEFAULT; + + if ((prop = fdt_getprop(_fdt, root, "#address-cells", RT_NULL))) + { + _root_addr_cells = fdt32_to_cpu(*prop); + } + + if ((prop = fdt_getprop(_fdt, root, "#size-cells", RT_NULL))) + { + _root_size_cells = fdt32_to_cpu(*prop); + } + } + else + { + err = -RT_EEMPTY; + } + + return err; +} + +rt_inline rt_err_t commit_memregion(const char *name, rt_uint64_t base, rt_uint64_t size, rt_bool_t is_reserved) +{ + return rt_fdt_commit_memregion_early(&(rt_region_t) + { + .name = name, + .start = (rt_size_t)base, + .end = (rt_size_t)(base + size), + }, is_reserved); +} + +static rt_err_t reserve_memregion(const char *name, rt_uint64_t base, rt_uint64_t size) +{ + if (commit_memregion(name, base, size, RT_TRUE) == -RT_EEMPTY) + { + LOG_W("Reserved memory: %p - %p%s", base, base + size, " unable to record"); + } + + return RT_EOK; +} + +static rt_err_t fdt_reserved_mem_check_root(int nodeoffset) +{ + rt_err_t err = RT_EOK; + const fdt32_t *prop = fdt_getprop(_fdt, nodeoffset, "#size-cells", RT_NULL); + + if (!prop || fdt32_to_cpu(*prop) != _root_size_cells) + { + err = -RT_EINVAL; + } + + if (!err) + { + prop = fdt_getprop(_fdt, nodeoffset, "#address-cells", RT_NULL); + + if (!prop || fdt32_to_cpu(*prop) != _root_addr_cells) + { + err = -RT_EINVAL; + } + } + + if (!err && !(prop = fdt_getprop(_fdt, nodeoffset, "ranges", RT_NULL))) + { + err = -RT_EINVAL; + } + + return err; +} + +static rt_err_t fdt_reserved_memory_reg(int nodeoffset, const char *uname) +{ + rt_err_t err = RT_EOK; + + rt_ubase_t base, size; + const fdt32_t *prop; + int len, t_len = (_root_addr_cells + _root_size_cells) * sizeof(fdt32_t); + + if ((prop = fdt_getprop(_fdt, nodeoffset, "reg", &len))) + { + if (len && len % t_len != 0) + { + LOG_E("Reserved memory: invalid reg property in '%s', skipping node", uname); + err = -RT_EINVAL; + } + else + { + while (len >= t_len) + { + base = rt_fdt_next_cell(&prop, _root_addr_cells); + size = rt_fdt_next_cell(&prop, _root_size_cells); + + if (!size) + { + continue; + } + + base = rt_fdt_translate_address(_fdt, nodeoffset, base); + reserve_memregion(fdt_get_name(_fdt, nodeoffset, RT_NULL), base, size); + + len -= t_len; + } + } + } + else + { + err = -RT_EEMPTY; + } + + return err; +} + +static void fdt_scan_reserved_memory(void) +{ + int nodeoffset, child; + + nodeoffset = fdt_path_offset(_fdt, "/reserved-memory"); + + if (nodeoffset >= 0) + { + if (!fdt_reserved_mem_check_root(nodeoffset)) + { + fdt_for_each_subnode(child, _fdt, nodeoffset) + { + rt_err_t err; + const char *uname; + + if (!rt_fdt_device_is_available(_fdt, child)) + { + continue; + } + + uname = fdt_get_name(_fdt, child, RT_NULL); + err = fdt_reserved_memory_reg(child, uname); + + if (err == -RT_EEMPTY && fdt_getprop(_fdt, child, "size", RT_NULL)) + { + reserve_memregion(fdt_get_name(_fdt, child, RT_NULL), 0, 0); + } + } + } + else + { + LOG_E("Reserved memory: unsupported node format, ignoring"); + } + } +} + +static rt_err_t fdt_scan_memory(void) +{ + int nodeoffset, no; + rt_region_t *region; + rt_uint64_t base, size; + rt_err_t err = -RT_EEMPTY; + + /* Process header /memreserve/ fields */ + for (no = 0; ; ++no) + { + fdt_get_mem_rsv(_fdt, no, &base, &size); + + if (!size) + { + break; + } + + reserve_memregion("memreserve", base, size); + } + + no = 0; + + fdt_for_each_subnode(nodeoffset, _fdt, 0) + { + int len; + const fdt32_t *reg, *endptr; + const char *name = fdt_get_name(_fdt, nodeoffset, RT_NULL); + const char *type = fdt_getprop(_fdt, nodeoffset, "device_type", RT_NULL); + + if (!type || rt_strcmp(type, "memory")) + { + continue; + } + + if (!rt_fdt_device_is_available(_fdt, nodeoffset)) + { + continue; + } + + reg = fdt_getprop(_fdt, nodeoffset, "reg", &len); + + if (!reg) + { + continue; + } + + endptr = reg + (len / sizeof(fdt32_t)); + name = name ? name : "memory"; + + while ((endptr - reg) >= (_root_addr_cells + _root_size_cells)) + { + base = rt_fdt_next_cell(®, _root_addr_cells); + size = rt_fdt_next_cell(®, _root_size_cells); + + if (!size) + { + continue; + } + + err = commit_memregion(name, base, size, RT_FALSE); + + if (!err) + { + LOG_I("Memory node(%d) ranges: %p - %p%s", no, base, base + size, ""); + } + else + { + LOG_W("Memory node(%d) ranges: %p - %p%s", no, base, base + size, " unable to record"); + } + + ++no; + } + } + + if (!err) + { + fdt_scan_reserved_memory(); + } + + region = &_memregion[0]; + + for (no = 0; region->name; ++region) + { + /* We need check the memory region now. */ + for (int i = RT_ARRAY_SIZE(_memregion) - 1; i > no; --i) + { + rt_region_t *res_region = &_memregion[i]; + + if (!res_region->name) + { + break; + } + + /* + * case 0: case 1: + * +------------------+ +----------+ + * | memory | | memory | + * +---+----------+---+ +---+----------+---+ + * | reserved | | reserved | + * +----------+ +---+----------+---+ + * + * case 2: case 3: + * +------------------+ +------------------+ + * | memory | | memory | + * +--------------+---+------+ +------+---+--------------+ + * | reserved | | reserved | + * +----------+ +----------+ + */ + + /* case 0 */ + if (res_region->start >= region->start && res_region->end <= region->end) + { + rt_size_t new_size = region->end - res_region->end; + + region->end = res_region->start; + + /* Commit part next block */ + if (new_size) + { + err = commit_memregion(region->name, res_region->end, new_size, RT_FALSE); + } + + if (!err) + { + ++no; + + /* Scan again */ + region = &_memregion[0]; + --region; + + break; + } + + continue; + } + + /* case 1 */ + if (res_region->start <= region->start && res_region->end >= region->end) + { + region->name = RT_NULL; + + break; + } + + /* case 2 */ + if (res_region->start <= region->end && res_region->end >= region->end) + { + region->end = res_region->start; + + continue; + } + + /* case 3 */ + if (res_region->start <= region->start && res_region->end >= region->start) + { + region->start = res_region->end; + + continue; + } + } + } + + return err; +} + +rt_err_t rt_fdt_scan_memory(void) +{ + rt_err_t err = -RT_EEMPTY; + + if (_fdt) + { + err = fdt_scan_memory(); + } + + return err; +} + +rt_err_t rt_fdt_scan_initrd(rt_uint64_t *ranges) +{ + rt_err_t err = -RT_EEMPTY; + + if (_fdt && ranges) + { + int s_len, e_len; + const fdt32_t *start = RT_NULL, *end = RT_NULL; + int offset = fdt_path_offset(_fdt, "/chosen"); + + if (offset >= 0) + { + start = fdt_getprop(_fdt, offset, "linux,initrd-start", &s_len); + end = fdt_getprop(_fdt, offset, "linux,initrd-end", &e_len); + } + + if (start && end) + { + s_len /= sizeof(*start); + e_len /= sizeof(*end); + + ranges[0] = rt_fdt_read_number(start, s_len); + ranges[1] = rt_fdt_read_number(end, e_len); + + commit_memregion("initrd", ranges[0], ranges[1] - ranges[0], RT_TRUE); + + err = RT_EOK; + } + } + else if (!ranges) + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_fdt_model_dump(void) +{ + rt_err_t err = RT_EOK; + int root = fdt_path_offset(_fdt, "/"); + + if (root >= 0) + { + const char *mach_model = fdt_getprop(_fdt, root, "model", RT_NULL); + + if (!mach_model) + { + mach_model = fdt_getprop(_fdt, root, "compatible", RT_NULL); + } + + LOG_I("Machine model: %s", mach_model ? mach_model : ""); + } + else + { + err = -RT_EEMPTY; + } + + return err; +} + +rt_weak rt_err_t rt_fdt_boot_dump(void) +{ + LOG_I("Booting RT-Thread on physical CPU 0x%x", rt_hw_cpu_id()); + + return RT_EOK; +} + +void rt_fdt_earlycon_output(const char *str) +{ + if (fdt_earlycon.console_putc) + { + while (*str) + { + fdt_earlycon.console_putc(fdt_earlycon.data, *str); + + if (*str == '\n') + { + /* Make sure return */ + fdt_earlycon.console_putc(fdt_earlycon.data, '\r'); + } + + ++str; + } + } + else + { + /* We need a byte to save '\0' */ + while (*str && fdt_earlycon.msg_idx < sizeof(fdt_earlycon.msg) - 1) + { + fdt_earlycon.msg[fdt_earlycon.msg_idx++] = *str; + + ++str; + } + fdt_earlycon.msg[fdt_earlycon.msg_idx] = '\0'; + } +} + +void rt_fdt_earlycon_kick(int why) +{ + if (fdt_earlycon.console_kick) + { + fdt_earlycon.console_kick(&fdt_earlycon, why); + } + + if (why == FDT_EARLYCON_KICK_COMPLETED && fdt_earlycon.msg_idx) + { + fdt_earlycon.msg_idx = 0; + + /* Dump old messages */ + rt_kputs(fdt_earlycon.msg); + } +} + +rt_err_t rt_fdt_scan_chosen_stdout(void) +{ + rt_err_t err = RT_EOK; + + int offset; + int len, options_len = 0; + const char *options = RT_NULL, *con_type = RT_NULL; + + rt_memset(&fdt_earlycon, 0, sizeof(fdt_earlycon) - sizeof(fdt_earlycon.msg)); + fdt_earlycon.nodeoffset = -1; + + offset = fdt_path_offset(_fdt, "/chosen"); + + if (offset >= 0) + { + const char *stdout_path = RT_NULL; + const char *bootargs = fdt_getprop(_fdt, offset, "bootargs", &len); + + if (bootargs && (options = rt_strstr(bootargs, "earlycon"))) + { + options += sizeof("earlycon") - 1; + + if (*options == '\0' || *options == ' ') + { + stdout_path = fdt_getprop(_fdt, offset, "stdout-path", &len); + + if (stdout_path && len) + { + const char *path_split = rt_strchrnul(stdout_path, ':'); + + if (*path_split != '\0') + { + options = path_split + 1; + } + + len = path_split - stdout_path; + + /* + * Will try 2 styles: + * 1: stdout-path = "serialN:bbbbpnf"; + * 2: stdout-path = "/serial-path"; + */ + offset = fdt_path_offset_namelen(_fdt, stdout_path, len); + + if (offset < 0) + { + stdout_path = RT_NULL; + } + } + else if (*options == '=') + { + ++options; + } + else + { + /* Maybe is error in bootargs or it is a new arg */ + options = RT_NULL; + } + + if (!stdout_path) + { + /* We couldn't know how to setup the earlycon */ + options = RT_NULL; + } + } + else + { + offset = -1; + } + + if (options) + { + int type_len = 0; + struct rt_fdt_earlycon_id *earlycon_id, *earlycon_id_end, *best_earlycon_id = RT_NULL; + + earlycon_id = (struct rt_fdt_earlycon_id *)&_earlycon_start; + earlycon_id_end = (struct rt_fdt_earlycon_id *)&_earlycon_end; + + err = -RT_ENOSYS; + + /* Only "earlycon" in bootargs */ + if (stdout_path) + { + const fdt32_t *reg; + options = RT_NULL; + + if ((reg = fdt_getprop(_fdt, offset, "reg", RT_NULL))) + { + rt_uint64_t address; + int addr_cells = fdt_io_addr_cells(_fdt, offset); + int size_cells = fdt_io_size_cells(_fdt, offset); + + address = rt_fdt_read_number(reg, addr_cells); + fdt_earlycon.mmio = rt_fdt_translate_address(_fdt, offset, address); + fdt_earlycon.size = rt_fdt_read_number(reg + addr_cells, size_cells); + } + } + else + { + /* Pass split */ + while (*options && (*options == '=' || *options == ' ')) + { + ++options; + } + + if (*options) + { + type_len = rt_strchrnul(options, ',') - options; + } + } + + if (options && *options && *options != ' ') + { + options_len = rt_strchrnul(options, ' ') - options; + } + + /* console > stdout-path */ + for (int max_score = 0; earlycon_id < earlycon_id_end; ++earlycon_id) + { + int score = 0; + + if (type_len && earlycon_id->type) + { + if (!rt_strncmp(earlycon_id->type, options, type_len)) + { + score += 1; + } + } + + if (stdout_path && earlycon_id->compatible) + { + if (!fdt_node_check_compatible(_fdt, offset, earlycon_id->compatible)) + { + score += 2; + } + } + + if (score > max_score) + { + max_score = score; + best_earlycon_id = earlycon_id; + + if (score == 3) + { + break; + } + } + } + + if (best_earlycon_id && best_earlycon_id->setup) + { + rt_bool_t used_options = RT_FALSE; + + if (!con_type) + { + con_type = best_earlycon_id->type; + } + fdt_earlycon.fdt = _fdt; + fdt_earlycon.nodeoffset = offset; + + err = best_earlycon_id->setup(&fdt_earlycon, options); + + for (int i = 0; i < options_len; ++i) + { + if (options[i] == RT_FDT_EARLYCON_OPTION_SIGNATURE) + { + /* Restore ',' */ + ((char *)options)[i++] = ','; + options = &options[i]; + options_len -= i; + used_options = RT_TRUE; + break; + } + } + if (!used_options) + { + options = RT_NULL; + options_len = 0; + } + } + } + } + else + { + err = -RT_EEMPTY; + } + } + else + { + err = -RT_EEMPTY; + } + + if (fdt_earlycon.msg_idx) + { + fdt_earlycon.msg_idx = 0; + + rt_kputs(fdt_earlycon.msg); + } + + rt_fdt_boot_dump(); + rt_fdt_model_dump(); + + if (fdt_earlycon.mmio) + { + LOG_I("Earlycon: %s at MMIO/PIO %p (options '%.*s')", + con_type, fdt_earlycon.mmio, options_len, options ? options : ""); + } + + return err; +} + +static void system_node_init_flag(struct rt_ofw_node *np) +{ + if (np) + { + rt_ofw_node_set_flag(np, RT_OFW_F_READLY); + rt_ofw_node_set_flag(np, RT_OFW_F_SYSTEM); + } +} + +rt_err_t rt_fdt_unflatten(void) +{ + rt_err_t err = RT_EOK; + + if (_fdt) + { + _phandle_min = OFW_PHANDLE_MAX; + _phandle_max = OFW_PHANDLE_MIN; + + ofw_node_root = rt_fdt_unflatten_single(_fdt); + + if (ofw_node_root) + { + ofw_node_cpus = rt_ofw_find_node_by_path("/cpus"); + ofw_node_chosen = rt_ofw_find_node_by_path("/chosen"); + ofw_node_aliases = rt_ofw_find_node_by_path("/aliases"); + ofw_node_reserved_memory = rt_ofw_find_node_by_path("/reserved-memory"); + + RT_ASSERT(ofw_node_cpus != RT_NULL); + + system_node_init_flag(ofw_node_root); + system_node_init_flag(ofw_node_cpus); + system_node_init_flag(ofw_node_chosen); + system_node_init_flag(ofw_node_aliases); + system_node_init_flag(ofw_node_reserved_memory); + + if (ofw_node_aliases) + { + err = ofw_alias_scan(); + } + + err = err ? : ofw_phandle_hash_reset(_phandle_min, _phandle_max); + } + } + else + { + err = -RT_ERROR; + } + + return err; +} + +static rt_err_t fdt_unflatten_props(struct rt_ofw_node *np, int node_off) +{ + rt_err_t err = RT_EOK; + struct rt_ofw_prop *prop; + int prop_off = fdt_first_property_offset(_fdt, node_off); + + if (prop_off >= 0) + { + np->props = rt_malloc(sizeof(struct rt_ofw_prop)); + } + + prop = np->props; + + while (prop_off >= 0) + { + if (!prop) + { + err = -RT_ENOMEM; + break; + } + + prop->value = (void *)fdt_getprop_by_offset(_fdt, prop_off, &prop->name, &prop->length); + + if (prop->name && !rt_strcmp(prop->name, "name")) + { + np->name = prop->value; + } + + prop_off = fdt_next_property_offset(_fdt, prop_off); + + if (prop_off < 0) + { + prop->next = RT_NULL; + break; + } + + prop->next = rt_malloc(sizeof(struct rt_ofw_prop)); + prop = prop->next; + } + + return err; +} + +static rt_err_t fdt_unflatten_single(struct rt_ofw_node *np, int node_off) +{ + int depth = 0; + rt_err_t err = RT_EOK; + struct rt_ofw_node *np_stack[OFW_NODE_MAX_DEPTH], *parent = RT_NULL; + + do { + if (!np) + { + err = -RT_ENOMEM; + break; + } + + np->name = ""; + np->full_name = fdt_get_name(_fdt, node_off, RT_NULL); + np->phandle = fdt_get_phandle(_fdt, node_off); + + if (np->phandle >= OFW_PHANDLE_MIN) + { + if (np->phandle < _phandle_min) + { + _phandle_min = np->phandle; + } + + if (np->phandle > _phandle_max) + { + _phandle_max = np->phandle; + } + } + + if ((err = fdt_unflatten_props(np, node_off))) + { + break; + } + + np->parent = parent; + + rt_ref_init(&np->ref); + np->flags = 0; + + if (!np->child) + { + /* Save node offset temp */ + rt_ofw_data(np) = (void *)(rt_ubase_t)node_off; + + /* Check children */ + node_off = fdt_first_subnode(_fdt, node_off); + + if (node_off >= 0) + { + parent = np; + + np_stack[depth++] = np; + + np->child = rt_calloc(1, sizeof(struct rt_ofw_node)); + np = np->child; + + continue; + } + } + + while (depth >= 0) + { + /* Restore node offset temp */ + node_off = (long)rt_ofw_data(np); + rt_ofw_data(np) = RT_NULL; + + /* Next step */ + node_off = fdt_next_subnode(_fdt, node_off); + + if (node_off < 0) + { + np->sibling = RT_NULL; + + np = np_stack[--depth]; + } + else + { + parent = np->parent; + + np->sibling = rt_calloc(1, sizeof(struct rt_ofw_node)); + np = np->sibling; + + break; + } + } + } while (depth >= 0); + + return err; +} + +struct rt_ofw_node *rt_fdt_unflatten_single(void *fdt) +{ + int root_off; + struct fdt_info *header; + struct rt_ofw_node *root = RT_NULL; + + if (fdt && (root_off = fdt_path_offset(fdt, "/")) >= 0) + { + root = rt_calloc(1, sizeof(struct fdt_info) + sizeof(struct rt_ofw_node)); + } + + if (root) + { + header = (void *)root + sizeof(struct rt_ofw_node); + + rt_strncpy(header->name, "/", sizeof("/")); + + header->fdt = fdt; + + header->rsvmap = (struct fdt_reserve_entry *)((void *)fdt + fdt_off_mem_rsvmap(fdt)); + header->rsvmap_nr = fdt_num_mem_rsv(fdt); + + if (!fdt_unflatten_single(root, root_off)) + { + root->name = (const char *)header; + } + else + { + rt_ofw_node_destroy(root); + + root = RT_NULL; + } + } + + return root; +} diff --git a/components/drivers/ofw/io.c b/components/drivers/ofw/io.c new file mode 100755 index 0000000000..b5d6b58425 --- /dev/null +++ b/components/drivers/ofw/io.c @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-25 GuEe-GUI first version + */ + +#include + +#include +#include +#include +#include + +#define DBG_TAG "rtdm.ofw" +#define DBG_LVL DBG_INFO +#include + +#include "ofw_internal.h" + +static int ofw_bus_addr_cells(struct rt_ofw_node *np) +{ + int res = OFW_ROOT_NODE_ADDR_CELLS_DEFAULT; + + for (rt_uint32_t cells; np; np = np->parent) + { + if (!rt_ofw_prop_read_u32(np, "#address-cells", &cells)) + { + res = cells; + break; + } + } + + return res; +} + +static int ofw_bus_size_cells(struct rt_ofw_node *np) +{ + int res = OFW_ROOT_NODE_SIZE_CELLS_DEFAULT; + + for (rt_uint32_t cells; np; np = np->parent) + { + if (!rt_ofw_prop_read_u32(np, "#size-cells", &cells)) + { + res = cells; + break; + } + } + + return res; +} + +int rt_ofw_bus_addr_cells(struct rt_ofw_node *np) +{ + return np ? ofw_bus_addr_cells(np) : -RT_EINVAL; +} + +int rt_ofw_bus_size_cells(struct rt_ofw_node *np) +{ + return np ? ofw_bus_size_cells(np) : -RT_EINVAL; +} + +int rt_ofw_io_addr_cells(struct rt_ofw_node *np) +{ + return np ? ofw_bus_addr_cells(np->parent ? np->parent : np) : -RT_EINVAL; +} + +int rt_ofw_io_size_cells(struct rt_ofw_node *np) +{ + return np ? ofw_bus_size_cells(np->parent ? np->parent : np) : -RT_EINVAL; +} + +int rt_ofw_get_address_count(struct rt_ofw_node *np) +{ + int count; + + if (np) + { + rt_ssize_t len; + + count = 0; + + if (rt_ofw_get_prop(np, "reg", &len)) + { + count = len / (sizeof(fdt32_t) * (rt_ofw_io_addr_cells(np) + rt_ofw_io_size_cells(np))); + } + } + else + { + count = -RT_EINVAL; + } + + return count; +} + +static rt_err_t ofw_get_address(struct rt_ofw_node *np, int index, rt_uint64_t *out_address, rt_uint64_t *out_size) +{ + rt_ssize_t len; + rt_err_t err = RT_EOK; + int addr_cells = rt_ofw_io_addr_cells(np); + int size_cells = rt_ofw_io_size_cells(np); + int skip_cells = (addr_cells + size_cells) * index; + const fdt32_t *cell = rt_ofw_prop_read_raw(np, "reg", &len); + + if (cell && skip_cells < (len / sizeof(*cell))) + { + cell += skip_cells; + *out_address = rt_fdt_next_cell(&cell, addr_cells); + *out_address = rt_ofw_translate_address(np, RT_NULL, *out_address); + *out_size = rt_fdt_read_number(cell, size_cells); + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_ofw_get_address(struct rt_ofw_node *np, int index, rt_uint64_t *out_address, rt_uint64_t *out_size) +{ + rt_err_t err; + + if (np && index >= 0 && (out_address || out_size)) + { + rt_uint64_t address, size; + + err = ofw_get_address(np, index, &address, &size); + + if (!err) + { + if (out_address) + { + *out_address = address; + } + if (out_size) + { + *out_size = size; + } + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +static rt_err_t ofw_get_address_by_name(struct rt_ofw_node *np, const char *name, + rt_uint64_t *out_address, rt_uint64_t *out_size) + +{ + int index = 0; + rt_err_t err = RT_EOK; + const char *reg_name; + struct rt_ofw_prop *prop; + + rt_ofw_foreach_prop_string(np, "reg-names", prop, reg_name) + { + if (!rt_strcmp(name, reg_name)) + { + err = rt_ofw_get_address(np, index, out_address, out_size); + + break; + } + + ++index; + } + + return err; +} + +rt_err_t rt_ofw_get_address_by_name(struct rt_ofw_node *np, const char *name, + rt_uint64_t *out_address, rt_uint64_t *out_size) +{ + rt_err_t err; + + if (np && name && (out_address || out_size)) + { + rt_uint64_t address, size; + + err = ofw_get_address_by_name(np, name, &address, &size); + + if (!err) + { + if (out_address) + { + *out_address = address; + } + if (out_size) + { + *out_size = size; + } + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +int rt_ofw_get_address_array(struct rt_ofw_node *np, int nr, rt_uint64_t *out_regs) +{ + int count; + + if (np && nr > 0 && out_regs) + { + rt_ssize_t len; + int max_nr; + int addr_cells = rt_ofw_io_addr_cells(np); + int size_cells = rt_ofw_io_size_cells(np); + const fdt32_t *cell = rt_ofw_prop_read_raw(np, "reg", &len); + + max_nr = len / (sizeof(*cell) * (addr_cells + size_cells)); + + if (nr > max_nr) + { + nr = max_nr; + } + + count = nr; + + while (nr --> 0) + { + *out_regs = rt_fdt_next_cell(&cell, addr_cells); + *out_regs = rt_ofw_translate_address(np, RT_NULL, *out_regs); + ++out_regs; + + *out_regs = rt_fdt_next_cell(&cell, size_cells); + ++out_regs; + } + } + else + { + count = -RT_EINVAL; + } + + return count; +} + +static struct bus_ranges *ofw_bus_ranges(struct rt_ofw_node *np, struct rt_ofw_prop *prop) +{ + const fdt32_t *cell; + struct bus_ranges *ranges = RT_NULL; + int child_address_cells, child_size_cells, parent_address_cells, groups; + rt_uint64_t *child_addr, *parent_addr, *child_size; + + /* + * Address Translation Example: + * + * / { + * #address-cells = <1>; + * #size-cells = <1>; + * + * soc { + * compatible = "simple-bus"; + * #address-cells = <1>; + * #size-cells = <1>; + * ranges = <0x0 0xe0000000 0x00100000>; + * + * serial@4600 { + * device_type = "serial"; + * reg = <0x4600 0x100>; + * clock-frequency = <0>; + * }; + * }; + * } + * + * The soc node specifies a ranges property of <0x0 0xe0000000 0x00100000>; + * This property value specifies that for a 1024 KB range of address space, a + * child node addressed at physical 0x0 maps to a parent address of physical + * 0xe0000000. With this mapping, the serial device node can be addressed by a + * load or store at address 0xe0004600, an offset of 0x4600 (specified in reg) + * plus the 0xe0000000 mapping specified in ranges: + * + * bus-address = parent-bus-address + (reg-address - child-bus-address) + */ + + do { + child_address_cells = rt_ofw_bus_addr_cells(np); + child_size_cells = rt_ofw_bus_size_cells(np); + parent_address_cells = rt_ofw_io_addr_cells(np); + + if (child_address_cells < 0 || child_size_cells < 0 || parent_address_cells < 0) + { + LOG_D("%s read address/size cells fail: child[%d, %d] parent[%d]", + np->full_name, child_address_cells, child_size_cells, parent_address_cells); + + break; + } + + groups = prop->length / sizeof(*cell); + groups /= child_address_cells + child_size_cells + parent_address_cells; + + ranges = rt_malloc(sizeof(*ranges) + sizeof(rt_uint64_t) * 3 * groups); + + if (!ranges) + { + break; + } + + ranges->nr = groups; + ranges->child_addr = (void *)ranges + sizeof(*ranges); + ranges->parent_addr = &ranges->child_addr[groups]; + ranges->child_size = &ranges->parent_addr[groups]; + + cell = prop->value; + + child_addr = ranges->child_addr; + parent_addr = ranges->parent_addr; + child_size = ranges->child_size; + + while (groups --> 0) + { + *child_addr++ = rt_fdt_next_cell(&cell, child_address_cells); + *parent_addr++ = rt_fdt_next_cell(&cell, parent_address_cells); + *child_size++ = rt_fdt_next_cell(&cell, child_size_cells); + } + + rt_ofw_data(np) = ranges; + } while (0); + + return ranges; +} + +rt_uint64_t rt_ofw_translate_address(struct rt_ofw_node *np, const char *range_type, rt_uint64_t address) +{ + rt_uint64_t cpu_addr = address; + + if (!range_type) + { + range_type = "ranges"; + } + + rt_ofw_foreach_parent_node(np) + { + rt_ssize_t len; + struct rt_ofw_prop *prop; + struct bus_ranges *ranges; + + prop = rt_ofw_get_prop(np, range_type, &len); + + if (!prop || !len) + { + continue; + } + + ranges = rt_ofw_data(np); + + if (!ranges) + { + ranges = ofw_bus_ranges(np, prop); + } + + if (ranges) + { + for (int i = 0; i < ranges->nr; ++i) + { + rt_uint64_t child_addr = ranges->child_addr[i]; + rt_uint64_t child_size = ranges->child_size[i]; + + if (address >= child_addr && address < child_addr + child_size) + { + cpu_addr = address + (ranges->parent_addr[i] - child_addr); + + break; + } + } + } + else + { + cpu_addr = ~0ULL; + } + + rt_ofw_node_put(np); + + break; + } + + return cpu_addr; +} + +void *rt_ofw_iomap(struct rt_ofw_node *np, int index) +{ + void *iomem = RT_NULL; + + if (np) + { + rt_uint64_t regs[2]; + + if (!ofw_get_address(np, index, ®s[0], ®s[1])) + { + iomem = rt_ioremap((void *)regs[0], (size_t)regs[1]); + } + } + + return iomem; +} + +void *rt_ofw_iomap_by_name(struct rt_ofw_node *np, const char *name) +{ + void *iomem = RT_NULL; + + if (np) + { + rt_uint64_t regs[2]; + + if (!ofw_get_address_by_name(np, name, ®s[0], ®s[1])) + { + iomem = rt_ioremap((void *)regs[0], (size_t)regs[1]); + } + } + + return iomem; +} diff --git a/components/drivers/ofw/irq.c b/components/drivers/ofw/irq.c new file mode 100755 index 0000000000..f5457453b4 --- /dev/null +++ b/components/drivers/ofw/irq.c @@ -0,0 +1,648 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-25 GuEe-GUI first version + */ + +#include + +#include +#include +#include +#include + +#define DBG_TAG "rtdm.ofw" +#define DBG_LVL DBG_INFO +#include + +#include "ofw_internal.h" + +static int ofw_interrupt_cells(struct rt_ofw_node *np) +{ + int interrupt_cells = -RT_EEMPTY; + + rt_ofw_prop_read_u32(np, "#interrupt-cells", (rt_uint32_t *)&interrupt_cells); + + return interrupt_cells; +} + +int rt_ofw_irq_cells(struct rt_ofw_node *np) +{ + return np ? ofw_interrupt_cells(np) : -RT_EINVAL; +} + +static rt_err_t ofw_parse_irq_map(struct rt_ofw_node *np, struct rt_ofw_cell_args *irq_args) +{ + rt_err_t err = RT_EOK; + rt_phandle ic_phandle = 0; + rt_ssize_t map_len, map_mask_len; + struct rt_ofw_node *ic_np = RT_NULL; + const fdt32_t *addr, *map, *map_mask; + int child_address_cells, child_interrupt_cells; + int parent_address_cells, parent_interrupt_cells; + int addr_cells, pin_cells, icaddr_cells, idx1, idx2, limit; + + /* + * interrupt-map: + * An interrupt-map is a property on a nexus node that bridges one + * interrupt domain with a set of parent interrupt domains and specifies + * how interrupt specifiers in the child domain are mapped to + * their respective parent domains. + * + * The interrupt map is a table where each row is a mapping entry + * consisting of five components: child unit address, child interrupt + * specifier, interrupt-parent, parent unit address, parent interrupt + * specifier. + * + * child unit address + * The unit address of the child node being mapped. The number of + * 32-bit cells required to specify this is described by the + * #address-cells property of the bus node on which the child is + * located. + * + * child interrupt specifier + * The interrupt specifier of the child node being mapped. The number + * of 32-bit cells required to specify this component is described by + * the #interrupt-cells property of this node—the nexus node containing + * the interrupt-map property. + * + * interrupt-parent + * A single value that points to the interrupt parent to + * which the child domain is being mapped. + * + * parent unit address + * The unit address in the domain of the interrupt parent. The number + * of 32-bit cells required to specify this address is described by the + * #address-cells property of the node pointed to by the + * interrupt-parent field. + * + * parent interrupt specifier + * The interrupt specifier in the parent domain. The number of 32-bit + * cells required to specify this component is described by the + * #interrupt-cells property of the node pointed to by the + * interrupt-parent field. + * + * Lookups are performed on the interrupt mapping table by matching a + * unit-address/interrupt specifier pair against the child components in + * the interrupt-map. Because some fields in the unit interrupt specifier + * may not be relevant, a mask is applied before the lookup is done. + * Example: + * + * pic: interrupt-controller@0 { + * interrupt-controller; + * #address-cells = <0>; // icaddr (parent unit address) + * #interrupt-cells = <1>; // icintr (parent interrupt specifier) + * }; + * + * gic: interrupt-controller@1 { + * interrupt-controller; + * #address-cells = <2>; // icaddr (parent unit address) + * #interrupt-cells = <3>; // icintr (parent interrupt specifier) + * }; + * + * pcie { + * #address-cells = <3>; // addr (child unit address) + * #interrupt-cells = <1>; // pin (child interrupt specifier) + * interrupt-parent = <&gic>; + * interrupt-map-mask = <0x1800 0 0 7>; + * interrupt-map = + * // addr pin ic icintr + * <0x0000 0 0 1 &pic 1>, // INTA SOLT 0 + * <0x0000 0 0 2 &pic 2>, // INTB + * <0x0000 0 0 3 &pic 3>, // INTC + * <0x0000 0 0 4 &pic 4>, // INTD + * <0x0800 0 0 1 &pic 2>, // INTA SOLT 1 + * <0x0800 0 0 2 &pic 3>, // INTB + * <0x0800 0 0 3 &pic 4>, // INTC + * <0x0800 0 0 4 &pic 1>, // INTD + * // addr pin ic icaddr icintr + * <0x1000 0 0 1 &gic 0 0 GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>, // INTA SOLT 2 + * <0x1000 0 0 2 &gic 0 0 GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>, // INTB + * <0x1000 0 0 3 &gic 0 0 GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>, // INTC + * <0x1000 0 0 4 &gic 0 0 GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>, // INTD + * <0x1800 0 0 1 &gic 0 0 GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>, // INTA SOLT 3 + * <0x1800 0 0 2 &gic 0 0 GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>, // INTB + * <0x1800 0 0 3 &gic 0 0 GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>, // INTC + * <0x1800 0 0 4 &gic 0 0 GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>; // INTD + * }; + * + * In fact, basically no SoC will be use multi ic to implemented INTx. + * before call ofw_parse_irq_map(np, &args): + * + * args.data = addr; + * args.args_count = 2 or 3; + * args.args[0] = (addr cells); + * args.args[1] = (pin cells); + * args.args[2] = (icaddr cells); + * + * if call with `pcie` in ofw_parse_irq_map(np, &args): + * + * np = &pcie; + * args.data = addr = fdt32_t({ (bus << 16) | (device << 11) | (function << 8), 0, 0, pin }); + * args.args_count = 2; + * args.args[0] = 3; + * args.args[1] = 1; + * + * To perform a lookup of the gic interrupt source number for INTB for IDSEL + * 0x12 (slot 2), function 0x3, the following steps would be performed: + * + * 1.The user addr is value <0x9300 0 0 2>. + * + * 2.The encoding of the address includes the bus number (0x0 << 16), + * device number (0x12 << 11), and function number (0x3 << 8). + * + * 3.The interrupt specifier is 2, which is the encoding for INTB as per + * the PCI binding. + * + * 4.The interrupt-map-mask value <0x1800 0 0 7> is applied, giving a + * result of <0x1000 0 0 2>. + * + * 5.That result is looked up in the interrupt-map table, which maps to the + * parent interrupt specifier . + */ + + do { + err = -RT_EEMPTY; + + if ((child_address_cells = rt_ofw_bus_addr_cells(np)) < 0) + { + LOG_D("%s property %s is undefined", np->full_name, "#address-cells"); + + break; + } + + if ((child_interrupt_cells = ofw_interrupt_cells(np)) < 0) + { + LOG_D("%s property %s is undefined", np->full_name, "#interrupt-cells"); + + break; + } + + if (!(map = rt_ofw_prop_read_raw(np, "interrupt-map", &map_len))) + { + LOG_D("%s property %s is undefined", np->full_name, "interrupt-map"); + + break; + } + + if (!(map_mask = rt_ofw_prop_read_raw(np, "interrupt-map-mask", &map_mask_len))) + { + LOG_D("%s property %s is undefined", np->full_name, "interrupt-map-mask"); + + break; + } + + err = -RT_EINVAL; + + addr = irq_args->data; + addr_cells = irq_args->args[0]; + pin_cells = irq_args->args[1]; + icaddr_cells = irq_args->args_count == 3 ? irq_args->args[2] : 0; + + if (addr_cells > child_address_cells) + { + LOG_D("%s(%d) > %s(%d)", "addr_cells", addr_cells, "child_address_cells", child_address_cells); + + break; + } + + if (pin_cells > child_interrupt_cells) + { + LOG_D("%s(%d) > %s(%d)", "pin_cells", pin_cells, "child_interrupt_cells", child_interrupt_cells); + + break; + } + + err = -RT_ENOENT; + +#define _map_walk_range(_idx, _idx2, _count, ...) \ + for (idx1 = _idx, idx2 = _idx2, limit = idx1 + _count; idx1 < limit __VA_ARGS__; ++idx1, ++idx2) + + _map_walk_range(0, 0, addr_cells) + { + /* Applied addr mask */ + ((fdt32_t *)addr)[idx1] &= map_mask[idx2]; + } + + _map_walk_range(addr_cells, child_address_cells, pin_cells) + { + /* Applied pin mask */ + ((fdt32_t *)addr)[idx1] &= map_mask[idx2]; + } + + while (map_len > 0) + { + rt_bool_t match = RT_TRUE; + + _map_walk_range(0, 0, addr_cells) + { + /* Applied mask */ + if (addr[idx1] != map[idx2]) + { + match = RT_FALSE; + break; + } + } + + _map_walk_range(addr_cells, child_address_cells, pin_cells, && match) + { + /* Applied mask */ + if (addr[idx1] != map[idx2]) + { + match = RT_FALSE; + break; + } + } + + /* Skip addr, pin */ + map += map_mask_len; + + /* IC is different? */ + if (ic_phandle != fdt32_to_cpu(*map)) + { + rt_ofw_node_put(ic_np); + + ic_phandle = fdt32_to_cpu(*map); + ic_np = rt_ofw_find_node_by_phandle(ic_phandle); + + if (!ic_np) + { + LOG_D("%s irq parent phandle = %d is not found", np->full_name, ic_phandle); + + break; + } + + if ((parent_address_cells = rt_ofw_bus_addr_cells(ic_np)) < 0) + { + LOG_D("%s property %s is undefined", ic_np->full_name, "#address-cells"); + + break; + } + + if (icaddr_cells > parent_address_cells) + { + LOG_D("%s(%d) > %s(%d)", "icaddr_cells", icaddr_cells, "parent_address_cells", parent_address_cells); + + break; + } + + if ((parent_interrupt_cells = ofw_interrupt_cells(ic_np)) < 0) + { + LOG_D("%s property %s is undefined", ic_np->full_name, "#interrupt-cells"); + + break; + } + + RT_ASSERT(parent_interrupt_cells <= RT_OFW_MAX_CELL_ARGS); + } + + /* Skip ic phandle */ + ++map; + + _map_walk_range(addr_cells + pin_cells, 0, icaddr_cells, && match) + { + /* Applied ic_addr mask */ + if (addr[idx1] != map[idx2]) + { + match = RT_FALSE; + break; + } + } + + /* Skip icaddr */ + map += parent_address_cells; + + if (match) + { + irq_args->data = ic_np; + irq_args->args_count = parent_interrupt_cells; + + for (int i = 0; i < irq_args->args_count; ++i) + { + irq_args->args[i] = fdt32_to_cpu(*map++); + } + + err = RT_EOK; + + break; + } + + /* Skip icintr */ + map += parent_interrupt_cells; + + map_len -= map_mask_len + 1 + parent_address_cells + parent_interrupt_cells; + } + +#undef _map_walk_range + } while (0); + + return err; +} + +rt_err_t rt_ofw_parse_irq_map(struct rt_ofw_node *np, struct rt_ofw_cell_args *irq_args) +{ + rt_err_t err; + + if (np && irq_args && irq_args->data) + { + err = ofw_parse_irq_map(np, irq_args); + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +static rt_err_t ofw_parse_irq_cells(struct rt_ofw_node *np, int index, struct rt_ofw_cell_args *out_irq_args) +{ + rt_err_t err; + + /* + * interrupts-extended: + * + * The interrupts-extended property lists the interrupt(s) generated by a + * device. interrupts-extended should be used instead of interrupts when a + * device is connected to multiple interrupt controllers as it encodes a + * parent phandle with each interrupt specifier. Example: + * + * pic: interrupt-controller@0 { + * interrupt-controller; + * #interrupt-cells = <1>; + * }; + * + * gic: interrupt-controller@1 { + * interrupt-controller; + * #interrupt-cells = <3>; + * }; + * + * node: node { + * interrupts-extended = <&pic 9>, <&gic GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>; + * }; + * + * call `rt_ofw_parse_phandle_cells` to get irq info; + */ + + err = rt_ofw_parse_phandle_cells(np, "interrupts-extended", "#interrupt-cells", index, out_irq_args); + + do { + int interrupt_cells; + const fdt32_t *cell; + rt_ssize_t interrupt_len; + struct rt_ofw_node *ic_np; + + if (!err) + { + break; + } + + /* + * interrupts (old style): + * + * The interrupts property of a device node defines the interrupt or + * interrupts that are generated by the device. The value of the + * interrupts property consists of an arbitrary number of interrupt + * specifiers. The format of an interrupt specifier is defined by the + * binding of the interrupt domain root. + * interrupts is overridden by the interrupts-extended property and + * normally only one or the other should be used. Example: + * + * pic: interrupt-controller@0 { + * interrupt-controller; + * #interrupt-cells = <1>; + * }; + * + * gic: interrupt-controller@1 { + * interrupt-controller; + * #interrupt-cells = <3>; + * }; + * + * node0: node0 { + * interrupt-parent = <&pic>; + * interrupts = <9>; + * }; + * + * node1: node1 { + * interrupt-parent = <&gic>; + * interrupts = ; + * }; + */ + + cell = rt_ofw_prop_read_raw(np, "interrupts", &interrupt_len); + + if (!cell) + { + err = -RT_ERROR; + break; + } + + ic_np = rt_ofw_find_irq_parent(np, &interrupt_cells); + + if (!ic_np) + { + err = -RT_ERROR; + break; + } + + RT_ASSERT(interrupt_cells <= RT_OFW_MAX_CELL_ARGS); + + if (index >= interrupt_len / (interrupt_cells * sizeof(*cell))) + { + err = -RT_EINVAL; + break; + } + + cell += index * interrupt_cells; + + out_irq_args->data = ic_np; + out_irq_args->args_count = interrupt_cells; + + for (int idx = 0; idx < interrupt_cells; ++idx, ++cell) + { + out_irq_args->args[idx] = fdt32_to_cpu(*cell); + } + + err = RT_EOK; + } while (0); + + return err; +} + +rt_err_t rt_ofw_parse_irq_cells(struct rt_ofw_node *np, int index, struct rt_ofw_cell_args *out_irq_args) +{ + rt_err_t err; + + if (np && index >= 0 && out_irq_args) + { + err = ofw_parse_irq_cells(np, index, out_irq_args); + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +struct rt_ofw_node *rt_ofw_find_irq_parent(struct rt_ofw_node *np, int *out_interrupt_cells) +{ + rt_ofw_foreach_parent_node(np) + { + rt_phandle ic_phandle; + + if (!rt_ofw_prop_read_u32(np, "interrupt-parent", (rt_uint32_t *)&ic_phandle)) + { + int interrupt_cells; + struct rt_ofw_node *ic_np = rt_ofw_find_node_by_phandle(ic_phandle); + + if (ic_np && (interrupt_cells = ofw_interrupt_cells(ic_np)) >= 0) + { + np = ic_np; + + if (out_interrupt_cells) + { + *out_interrupt_cells = interrupt_cells; + } + + break; + } + + rt_ofw_node_put(ic_np); + } + } + + return np; +} + +static int ofw_map_irq(struct rt_ofw_cell_args *irq_args) +{ + int irq; + struct rt_ofw_node *ic_np = irq_args->data; + struct rt_pic *pic = rt_ofw_data(ic_np); + + /* args.data is "interrupt-controller" */ + if (pic) + { + struct rt_pic_irq pirq; + + if (!pic->ops->irq_parse) + { + LOG_E("Master pic MUST implemented irq_parse"); + RT_ASSERT(0); + } + + if (!pic->ops->irq_map) + { + LOG_E("Master pic MUST implemented irq_map"); + RT_ASSERT(0); + } + + irq = pic->ops->irq_parse(pic, irq_args, &pirq); + + if (!irq) + { + irq = pic->ops->irq_map(pic, pirq.hwirq, pirq.mode); + } + } + else + { + LOG_E("Master pic %s not support", ic_np->full_name); + irq = -RT_EIO; + } + + rt_ofw_node_put(ic_np); + + return irq; +} + +int rt_ofw_map_irq(struct rt_ofw_cell_args *irq_args) +{ + int irq; + + if (irq_args && irq_args->data && irq_args->args_count > 0) + { + irq = ofw_map_irq(irq_args); + } + else + { + irq = -RT_EINVAL; + } + + return irq; +} + +int rt_ofw_get_irq_count(struct rt_ofw_node *np) +{ + int count; + + if (np) + { + struct rt_ofw_cell_args irq_args; + + count = 0; + + while (!ofw_parse_irq_cells(np, count, &irq_args)) + { + ++count; + } + } + else + { + count = -RT_EINVAL; + } + + return count; +} + +int rt_ofw_get_irq(struct rt_ofw_node *np, int index) +{ + int irq; + + if (np && index >= 0) + { + struct rt_ofw_cell_args irq_args; + + irq = ofw_parse_irq_cells(np, index, &irq_args); + + if (irq >= 0) + { + irq = ofw_map_irq(&irq_args); + } + } + else + { + irq = -RT_EINVAL; + } + + return irq; +} + +int rt_ofw_get_irq_by_name(struct rt_ofw_node *np, const char *name) +{ + int irq; + + if (np && name) + { + int index = rt_ofw_prop_index_of_string(np, "interrupt-names", name); + + if (index >= 0) + { + irq = rt_ofw_get_irq(np, index); + } + else + { + irq = -1; + } + } + else + { + irq = -RT_EINVAL; + } + + return irq; +} diff --git a/components/drivers/ofw/libfdt/SConscript b/components/drivers/ofw/libfdt/SConscript new file mode 100644 index 0000000000..fb1fe97f3b --- /dev/null +++ b/components/drivers/ofw/libfdt/SConscript @@ -0,0 +1,10 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + +CPPPATH = [cwd] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/ofw/libfdt/fdt.c b/components/drivers/ofw/libfdt/fdt.c new file mode 100644 index 0000000000..20c6415b9c --- /dev/null +++ b/components/drivers/ofw/libfdt/fdt.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +/* + * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks + * that the given buffer contains what appears to be a flattened + * device tree with sane information in its header. + */ +int32_t fdt_ro_probe_(const void *fdt) +{ + uint32_t totalsize = fdt_totalsize(fdt); + + if (can_assume(VALID_DTB)) + return totalsize; + + /* The device tree must be at an 8-byte aligned address */ + if ((uintptr_t)fdt & 7) + return -FDT_ERR_ALIGNMENT; + + if (fdt_magic(fdt) == FDT_MAGIC) { + /* Complete tree */ + if (!can_assume(LATEST)) { + 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 (!can_assume(VALID_INPUT) && fdt_size_dt_struct(fdt) == 0) + return -FDT_ERR_BADSTATE; + } else { + return -FDT_ERR_BADMAGIC; + } + + if (totalsize < INT32_MAX) + return totalsize; + else + return -FDT_ERR_TRUNCATED; +} + +static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off) +{ + return (off >= hdrsize) && (off <= totalsize); +} + +static int check_block_(uint32_t hdrsize, uint32_t totalsize, + uint32_t base, uint32_t size) +{ + if (!check_off_(hdrsize, totalsize, base)) + return 0; /* block start out of bounds */ + if ((base + size) < base) + return 0; /* overflow */ + if (!check_off_(hdrsize, totalsize, base + size)) + return 0; /* block end out of bounds */ + return 1; +} + +size_t fdt_header_size_(uint32_t version) +{ + if (version <= 1) + return FDT_V1_SIZE; + else if (version <= 2) + return FDT_V2_SIZE; + else if (version <= 3) + return FDT_V3_SIZE; + else if (version <= 16) + return FDT_V16_SIZE; + else + return FDT_V17_SIZE; +} + +size_t fdt_header_size(const void *fdt) +{ + return can_assume(LATEST) ? FDT_V17_SIZE : + fdt_header_size_(fdt_version(fdt)); +} + +int fdt_check_header(const void *fdt) +{ + size_t hdrsize; + + /* The device tree must be at an 8-byte aligned address */ + if ((uintptr_t)fdt & 7) + return -FDT_ERR_ALIGNMENT; + + if (fdt_magic(fdt) != FDT_MAGIC) + return -FDT_ERR_BADMAGIC; + if (!can_assume(LATEST)) { + if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) + || (fdt_last_comp_version(fdt) > + FDT_LAST_SUPPORTED_VERSION)) + return -FDT_ERR_BADVERSION; + if (fdt_version(fdt) < fdt_last_comp_version(fdt)) + return -FDT_ERR_BADVERSION; + } + hdrsize = fdt_header_size(fdt); + if (!can_assume(VALID_DTB)) { + if ((fdt_totalsize(fdt) < hdrsize) + || (fdt_totalsize(fdt) > INT_MAX)) + return -FDT_ERR_TRUNCATED; + + /* Bounds check memrsv block */ + if (!check_off_(hdrsize, fdt_totalsize(fdt), + fdt_off_mem_rsvmap(fdt))) + return -FDT_ERR_TRUNCATED; + + /* Bounds check structure block */ + if (!can_assume(LATEST) && fdt_version(fdt) < 17) { + if (!check_off_(hdrsize, fdt_totalsize(fdt), + fdt_off_dt_struct(fdt))) + return -FDT_ERR_TRUNCATED; + } else { + if (!check_block_(hdrsize, fdt_totalsize(fdt), + fdt_off_dt_struct(fdt), + fdt_size_dt_struct(fdt))) + return -FDT_ERR_TRUNCATED; + } + + /* Bounds check strings block */ + if (!check_block_(hdrsize, fdt_totalsize(fdt), + fdt_off_dt_strings(fdt), + fdt_size_dt_strings(fdt))) + return -FDT_ERR_TRUNCATED; + } + + return 0; +} + +const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) +{ + unsigned int uoffset = offset; + unsigned int absoffset = offset + fdt_off_dt_struct(fdt); + + if (offset < 0) + return NULL; + + if (!can_assume(VALID_INPUT)) + if ((absoffset < uoffset) + || ((absoffset + len) < absoffset) + || (absoffset + len) > fdt_totalsize(fdt)) + return NULL; + + if (can_assume(LATEST) || fdt_version(fdt) >= 0x11) + if (((uoffset + len) < uoffset) + || ((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, len, sum; + int offset = startoffset; + const char *p; + + *nextoffset = -FDT_ERR_TRUNCATED; + tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); + if (!can_assume(VALID_DTB) && !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 (!can_assume(VALID_DTB) && !p) + return FDT_END; /* premature end */ + break; + + case FDT_PROP: + lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); + if (!can_assume(VALID_DTB) && !lenp) + return FDT_END; /* premature end */ + + len = fdt32_to_cpu(*lenp); + sum = len + offset; + if (!can_assume(VALID_DTB) && + (INT_MAX <= sum || sum < (uint32_t) offset)) + return FDT_END; /* premature end */ + + /* skip-name offset, length and value */ + offset += sizeof(struct fdt_property) - FDT_TAGSIZE + len; + + if (!can_assume(LATEST) && + fdt_version(fdt) < 0x10 && len >= 8 && + ((offset - len) % 8) != 0) + offset += 4; + 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 (!can_assume(VALID_INPUT) + && ((offset < 0) || (offset % FDT_TAGSIZE))) + return -FDT_ERR_BADOFFSET; + + if (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 (!can_assume(VALID_INPUT) + && ((offset < 0) || (offset % FDT_TAGSIZE))) + return -FDT_ERR_BADOFFSET; + + if (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) +{ + if (!can_assume(VALID_INPUT) && bufsize < 0) + return -FDT_ERR_NOSPACE; + + FDT_RO_PROBE(fdt); + + if (fdt_totalsize(fdt) > (unsigned int)bufsize) + return -FDT_ERR_NOSPACE; + + memmove(buf, fdt, fdt_totalsize(fdt)); + return 0; +} diff --git a/components/drivers/ofw/libfdt/fdt.h b/components/drivers/ofw/libfdt/fdt.h new file mode 100644 index 0000000000..0c91aa7f67 --- /dev/null +++ b/components/drivers/ofw/libfdt/fdt.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ +#ifndef FDT_H +#define FDT_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * Copyright 2012 Kim Phillips, Freescale Semiconductor. + */ + +#ifndef __ASSEMBLY__ + +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[]; +}; + +struct fdt_property { + fdt32_t tag; + fdt32_t len; + fdt32_t nameoff; + char data[]; +}; + +#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 */ diff --git a/components/drivers/ofw/libfdt/fdt_addresses.c b/components/drivers/ofw/libfdt/fdt_addresses.c new file mode 100644 index 0000000000..c40ba094f1 --- /dev/null +++ b/components/drivers/ofw/libfdt/fdt_addresses.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2014 David Gibson + * Copyright (C) 2018 embedded brains GmbH + */ +#include "libfdt_env.h" + +#include +#include + +#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) || (((uint64_t) 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)); +} diff --git a/components/drivers/ofw/libfdt/fdt_empty_tree.c b/components/drivers/ofw/libfdt/fdt_empty_tree.c new file mode 100644 index 0000000000..49d54d44b8 --- /dev/null +++ b/components/drivers/ofw/libfdt/fdt_empty_tree.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2012 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include +#include + +#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); +} diff --git a/components/drivers/ofw/libfdt/fdt_overlay.c b/components/drivers/ofw/libfdt/fdt_overlay.c new file mode 100644 index 0000000000..5c0c3981b8 --- /dev/null +++ b/components/drivers/ofw/libfdt/fdt_overlay.c @@ -0,0 +1,867 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2016 Free Electrons + * Copyright (C) 2016 NextThing Co. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +/** + * overlay_get_target_phandle - retrieves the target phandle of a fragment + * @fdto: pointer to the device tree overlay blob + * @fragment: node offset of the fragment in the overlay + * + * overlay_get_target_phandle() retrieves the target phandle of an + * overlay fragment when that fragment uses a phandle (target + * property) instead of a path (target-path property). + * + * returns: + * the phandle pointed by the target property + * 0, if the phandle was not found + * -1, if the phandle was malformed + */ +static uint32_t overlay_get_target_phandle(const void *fdto, int fragment) +{ + const fdt32_t *val; + int len; + + val = fdt_getprop(fdto, fragment, "target", &len); + if (!val) + return 0; + + if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1)) + return (uint32_t)-1; + + return fdt32_to_cpu(*val); +} + +int fdt_overlay_target_offset(const void *fdt, const void *fdto, + int fragment_offset, char const **pathp) +{ + uint32_t phandle; + const char *path = NULL; + int path_len = 0, ret; + + /* Try first to do a phandle based lookup */ + phandle = overlay_get_target_phandle(fdto, fragment_offset); + if (phandle == (uint32_t)-1) + return -FDT_ERR_BADPHANDLE; + + /* no phandle, try path */ + if (!phandle) { + /* And then a path based lookup */ + path = fdt_getprop(fdto, fragment_offset, "target-path", &path_len); + if (path) + ret = fdt_path_offset(fdt, path); + else + ret = path_len; + } else + ret = fdt_node_offset_by_phandle(fdt, phandle); + + /* + * If we haven't found either a target or a + * target-path property in a node that contains a + * __overlay__ subnode (we wouldn't be called + * otherwise), consider it a improperly written + * overlay + */ + if (ret < 0 && path_len == -FDT_ERR_NOTFOUND) + ret = -FDT_ERR_BADOVERLAY; + + /* return on error */ + if (ret < 0) + return ret; + + /* return pointer to path (if available) */ + if (pathp) + *pathp = path ? path : NULL; + + return ret; +} + +/** + * overlay_phandle_add_offset - Increases a phandle by an offset + * @fdt: Base device tree blob + * @node: Device tree overlay blob + * @name: Name of the property to modify (phandle or linux,phandle) + * @delta: offset to apply + * + * overlay_phandle_add_offset() increments a node phandle by a given + * offset. + * + * returns: + * 0 on success. + * Negative error code on error + */ +static int overlay_phandle_add_offset(void *fdt, int node, + const char *name, uint32_t delta) +{ + const fdt32_t *val; + uint32_t adj_val; + int len; + + val = fdt_getprop(fdt, node, name, &len); + if (!val) + return len; + + if (len != sizeof(*val)) + return -FDT_ERR_BADPHANDLE; + + adj_val = fdt32_to_cpu(*val); + if ((adj_val + delta) < adj_val) + return -FDT_ERR_NOPHANDLES; + + adj_val += delta; + if (adj_val == (uint32_t)-1) + return -FDT_ERR_NOPHANDLES; + + return fdt_setprop_inplace_u32(fdt, node, name, adj_val); +} + +/** + * overlay_adjust_node_phandles - Offsets the phandles of a node + * @fdto: Device tree overlay blob + * @node: Offset of the node we want to adjust + * @delta: Offset to shift the phandles of + * + * overlay_adjust_node_phandles() adds a constant to all the phandles + * of a given node. This is mainly use as part of the overlay + * application process, when we want to update all the overlay + * phandles to not conflict with the overlays of the base device tree. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_adjust_node_phandles(void *fdto, int node, + uint32_t delta) +{ + int child; + int ret; + + ret = overlay_phandle_add_offset(fdto, node, "phandle", delta); + if (ret && ret != -FDT_ERR_NOTFOUND) + return ret; + + ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta); + if (ret && ret != -FDT_ERR_NOTFOUND) + return ret; + + fdt_for_each_subnode(child, fdto, node) { + ret = overlay_adjust_node_phandles(fdto, child, delta); + if (ret) + return ret; + } + + return 0; +} + +/** + * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay + * @fdto: Device tree overlay blob + * @delta: Offset to shift the phandles of + * + * overlay_adjust_local_phandles() adds a constant to all the + * phandles of an overlay. This is mainly use as part of the overlay + * application process, when we want to update all the overlay + * phandles to not conflict with the overlays of the base device tree. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_adjust_local_phandles(void *fdto, uint32_t delta) +{ + /* + * Start adjusting the phandles from the overlay root + */ + return overlay_adjust_node_phandles(fdto, 0, delta); +} + +/** + * overlay_update_local_node_references - Adjust the overlay references + * @fdto: Device tree overlay blob + * @tree_node: Node offset of the node to operate on + * @fixup_node: Node offset of the matching local fixups node + * @delta: Offset to shift the phandles of + * + * overlay_update_local_nodes_references() update the phandles + * pointing to a node within the device tree overlay by adding a + * constant delta. + * + * This is mainly used as part of a device tree application process, + * where you want the device tree overlays phandles to not conflict + * with the ones from the base device tree before merging them. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_update_local_node_references(void *fdto, + int tree_node, + int fixup_node, + uint32_t delta) +{ + int fixup_prop; + int fixup_child; + int ret; + + fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) { + const fdt32_t *fixup_val; + const char *tree_val; + const char *name; + int fixup_len; + int tree_len; + int i; + + fixup_val = fdt_getprop_by_offset(fdto, fixup_prop, + &name, &fixup_len); + if (!fixup_val) + return fixup_len; + + if (fixup_len % sizeof(uint32_t)) + return -FDT_ERR_BADOVERLAY; + fixup_len /= sizeof(uint32_t); + + tree_val = fdt_getprop(fdto, tree_node, name, &tree_len); + if (!tree_val) { + if (tree_len == -FDT_ERR_NOTFOUND) + return -FDT_ERR_BADOVERLAY; + + return tree_len; + } + + for (i = 0; i < fixup_len; i++) { + fdt32_t adj_val; + uint32_t poffset; + + poffset = fdt32_to_cpu(fixup_val[i]); + + /* + * phandles to fixup can be unaligned. + * + * Use a memcpy for the architectures that do + * not support unaligned accesses. + */ + memcpy(&adj_val, tree_val + poffset, sizeof(adj_val)); + + adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta); + + ret = fdt_setprop_inplace_namelen_partial(fdto, + tree_node, + name, + strlen(name), + poffset, + &adj_val, + sizeof(adj_val)); + if (ret == -FDT_ERR_NOSPACE) + return -FDT_ERR_BADOVERLAY; + + if (ret) + return ret; + } + } + + fdt_for_each_subnode(fixup_child, fdto, fixup_node) { + const char *fixup_child_name = fdt_get_name(fdto, fixup_child, + NULL); + int tree_child; + + tree_child = fdt_subnode_offset(fdto, tree_node, + fixup_child_name); + if (tree_child == -FDT_ERR_NOTFOUND) + return -FDT_ERR_BADOVERLAY; + if (tree_child < 0) + return tree_child; + + ret = overlay_update_local_node_references(fdto, + tree_child, + fixup_child, + delta); + if (ret) + return ret; + } + + return 0; +} + +/** + * overlay_update_local_references - Adjust the overlay references + * @fdto: Device tree overlay blob + * @delta: Offset to shift the phandles of + * + * overlay_update_local_references() update all the phandles pointing + * to a node within the device tree overlay by adding a constant + * delta to not conflict with the base overlay. + * + * This is mainly used as part of a device tree application process, + * where you want the device tree overlays phandles to not conflict + * with the ones from the base device tree before merging them. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_update_local_references(void *fdto, uint32_t delta) +{ + int fixups; + + fixups = fdt_path_offset(fdto, "/__local_fixups__"); + if (fixups < 0) { + /* There's no local phandles to adjust, bail out */ + if (fixups == -FDT_ERR_NOTFOUND) + return 0; + + return fixups; + } + + /* + * Update our local references from the root of the tree + */ + return overlay_update_local_node_references(fdto, 0, fixups, + delta); +} + +/** + * overlay_fixup_one_phandle - Set an overlay phandle to the base one + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * @symbols_off: Node offset of the symbols node in the base device tree + * @path: Path to a node holding a phandle in the overlay + * @path_len: number of path characters to consider + * @name: Name of the property holding the phandle reference in the overlay + * @name_len: number of name characters to consider + * @poffset: Offset within the overlay property where the phandle is stored + * @label: Label of the node referenced by the phandle + * + * overlay_fixup_one_phandle() resolves an overlay phandle pointing to + * a node in the base device tree. + * + * This is part of the device tree overlay application process, when + * you want all the phandles in the overlay to point to the actual + * base dt nodes. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_fixup_one_phandle(void *fdt, void *fdto, + int symbols_off, + const char *path, uint32_t path_len, + const char *name, uint32_t name_len, + int poffset, const char *label) +{ + const char *symbol_path; + uint32_t phandle; + fdt32_t phandle_prop; + int symbol_off, fixup_off; + int prop_len; + + if (symbols_off < 0) + return symbols_off; + + symbol_path = fdt_getprop(fdt, symbols_off, label, + &prop_len); + if (!symbol_path) + return prop_len; + + symbol_off = fdt_path_offset(fdt, symbol_path); + if (symbol_off < 0) + return symbol_off; + + phandle = fdt_get_phandle(fdt, symbol_off); + if (!phandle) + return -FDT_ERR_NOTFOUND; + + fixup_off = fdt_path_offset_namelen(fdto, path, path_len); + if (fixup_off == -FDT_ERR_NOTFOUND) + return -FDT_ERR_BADOVERLAY; + if (fixup_off < 0) + return fixup_off; + + phandle_prop = cpu_to_fdt32(phandle); + return fdt_setprop_inplace_namelen_partial(fdto, fixup_off, + name, name_len, poffset, + &phandle_prop, + sizeof(phandle_prop)); +}; + +/** + * overlay_fixup_phandle - Set an overlay phandle to the base one + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * @symbols_off: Node offset of the symbols node in the base device tree + * @property: Property offset in the overlay holding the list of fixups + * + * overlay_fixup_phandle() resolves all the overlay phandles pointed + * to in a __fixups__ property, and updates them to match the phandles + * in use in the base device tree. + * + * This is part of the device tree overlay application process, when + * you want all the phandles in the overlay to point to the actual + * base dt nodes. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, + int property) +{ + const char *value; + const char *label; + int len; + + value = fdt_getprop_by_offset(fdto, property, + &label, &len); + if (!value) { + if (len == -FDT_ERR_NOTFOUND) + return -FDT_ERR_INTERNAL; + + return len; + } + + do { + const char *path, *name, *fixup_end; + const char *fixup_str = value; + uint32_t path_len, name_len; + uint32_t fixup_len; + char *sep, *endptr; + int poffset, ret; + + fixup_end = memchr(value, '\0', len); + if (!fixup_end) + return -FDT_ERR_BADOVERLAY; + fixup_len = fixup_end - fixup_str; + + len -= fixup_len + 1; + value += fixup_len + 1; + + path = fixup_str; + sep = memchr(fixup_str, ':', fixup_len); + if (!sep || *sep != ':') + return -FDT_ERR_BADOVERLAY; + + path_len = sep - path; + if (path_len == (fixup_len - 1)) + return -FDT_ERR_BADOVERLAY; + + fixup_len -= path_len + 1; + name = sep + 1; + sep = memchr(name, ':', fixup_len); + if (!sep || *sep != ':') + return -FDT_ERR_BADOVERLAY; + + name_len = sep - name; + if (!name_len) + return -FDT_ERR_BADOVERLAY; + + poffset = strtoul(sep + 1, &endptr, 10); + if ((*endptr != '\0') || (endptr <= (sep + 1))) + return -FDT_ERR_BADOVERLAY; + + ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off, + path, path_len, name, name_len, + poffset, label); + if (ret) + return ret; + } while (len > 0); + + return 0; +} + +/** + * overlay_fixup_phandles - Resolve the overlay phandles to the base + * device tree + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * + * overlay_fixup_phandles() resolves all the overlay phandles pointing + * to nodes in the base device tree. + * + * This is one of the steps of the device tree overlay application + * process, when you want all the phandles in the overlay to point to + * the actual base dt nodes. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_fixup_phandles(void *fdt, void *fdto) +{ + int fixups_off, symbols_off; + int property; + + /* We can have overlays without any fixups */ + fixups_off = fdt_path_offset(fdto, "/__fixups__"); + if (fixups_off == -FDT_ERR_NOTFOUND) + return 0; /* nothing to do */ + if (fixups_off < 0) + return fixups_off; + + /* And base DTs without symbols */ + symbols_off = fdt_path_offset(fdt, "/__symbols__"); + if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND))) + return symbols_off; + + fdt_for_each_property_offset(property, fdto, fixups_off) { + int ret; + + ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property); + if (ret) + return ret; + } + + return 0; +} + +/** + * overlay_apply_node - Merges a node into the base device tree + * @fdt: Base Device Tree blob + * @target: Node offset in the base device tree to apply the fragment to + * @fdto: Device tree overlay blob + * @node: Node offset in the overlay holding the changes to merge + * + * overlay_apply_node() merges a node into a target base device tree + * node pointed. + * + * This is part of the final step in the device tree overlay + * application process, when all the phandles have been adjusted and + * resolved and you just have to merge overlay into the base device + * tree. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_apply_node(void *fdt, int target, + void *fdto, int node) +{ + int property; + int subnode; + + fdt_for_each_property_offset(property, fdto, node) { + const char *name; + const void *prop; + int prop_len; + int ret; + + prop = fdt_getprop_by_offset(fdto, property, &name, + &prop_len); + if (prop_len == -FDT_ERR_NOTFOUND) + return -FDT_ERR_INTERNAL; + if (prop_len < 0) + return prop_len; + + ret = fdt_setprop(fdt, target, name, prop, prop_len); + if (ret) + return ret; + } + + fdt_for_each_subnode(subnode, fdto, node) { + const char *name = fdt_get_name(fdto, subnode, NULL); + int nnode; + int ret; + + nnode = fdt_add_subnode(fdt, target, name); + if (nnode == -FDT_ERR_EXISTS) { + nnode = fdt_subnode_offset(fdt, target, name); + if (nnode == -FDT_ERR_NOTFOUND) + return -FDT_ERR_INTERNAL; + } + + if (nnode < 0) + return nnode; + + ret = overlay_apply_node(fdt, nnode, fdto, subnode); + if (ret) + return ret; + } + + return 0; +} + +/** + * overlay_merge - Merge an overlay into its base device tree + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * + * overlay_merge() merges an overlay into its base device tree. + * + * This is the next to last step in the device tree overlay application + * process, when all the phandles have been adjusted and resolved and + * you just have to merge overlay into the base device tree. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_merge(void *fdt, void *fdto) +{ + int fragment; + + fdt_for_each_subnode(fragment, fdto, 0) { + int overlay; + int target; + int ret; + + /* + * Each fragments will have an __overlay__ node. If + * they don't, it's not supposed to be merged + */ + overlay = fdt_subnode_offset(fdto, fragment, "__overlay__"); + if (overlay == -FDT_ERR_NOTFOUND) + continue; + + if (overlay < 0) + return overlay; + + target = fdt_overlay_target_offset(fdt, fdto, fragment, NULL); + if (target < 0) + return target; + + ret = overlay_apply_node(fdt, target, fdto, overlay); + if (ret) + return ret; + } + + return 0; +} + +static int get_path_len(const void *fdt, int nodeoffset) +{ + int len = 0, namelen; + const char *name; + + FDT_RO_PROBE(fdt); + + for (;;) { + name = fdt_get_name(fdt, nodeoffset, &namelen); + if (!name) + return namelen; + + /* root? we're done */ + if (namelen == 0) + break; + + nodeoffset = fdt_parent_offset(fdt, nodeoffset); + if (nodeoffset < 0) + return nodeoffset; + len += namelen + 1; + } + + /* in case of root pretend it's "/" */ + if (len == 0) + len++; + return len; +} + +/** + * overlay_symbol_update - Update the symbols of base tree after a merge + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * + * overlay_symbol_update() updates the symbols of the base tree with the + * symbols of the applied overlay + * + * This is the last step in the device tree overlay application + * process, allowing the reference of overlay symbols by subsequent + * overlay operations. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_symbol_update(void *fdt, void *fdto) +{ + int root_sym, ov_sym, prop, path_len, fragment, target; + int len, frag_name_len, ret, rel_path_len; + const char *s, *e; + const char *path; + const char *name; + const char *frag_name; + const char *rel_path; + const char *target_path; + char *buf; + void *p; + + ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__"); + + /* if no overlay symbols exist no problem */ + if (ov_sym < 0) + return 0; + + root_sym = fdt_subnode_offset(fdt, 0, "__symbols__"); + + /* it no root symbols exist we should create them */ + if (root_sym == -FDT_ERR_NOTFOUND) + root_sym = fdt_add_subnode(fdt, 0, "__symbols__"); + + /* any error is fatal now */ + if (root_sym < 0) + return root_sym; + + /* iterate over each overlay symbol */ + fdt_for_each_property_offset(prop, fdto, ov_sym) { + path = fdt_getprop_by_offset(fdto, prop, &name, &path_len); + if (!path) + return path_len; + + /* verify it's a string property (terminated by a single \0) */ + if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1]) + return -FDT_ERR_BADVALUE; + + /* keep end marker to avoid strlen() */ + e = path + path_len; + + if (*path != '/') + return -FDT_ERR_BADVALUE; + + /* get fragment name first */ + s = strchr(path + 1, '/'); + if (!s) { + /* Symbol refers to something that won't end + * up in the target tree */ + continue; + } + + frag_name = path + 1; + frag_name_len = s - path - 1; + + /* verify format; safe since "s" lies in \0 terminated prop */ + len = sizeof("/__overlay__/") - 1; + if ((e - s) > len && (memcmp(s, "/__overlay__/", len) == 0)) { + /* //__overlay__/ */ + rel_path = s + len; + rel_path_len = e - rel_path - 1; + } else if ((e - s) == len + && (memcmp(s, "/__overlay__", len - 1) == 0)) { + /* //__overlay__ */ + rel_path = ""; + rel_path_len = 0; + } else { + /* Symbol refers to something that won't end + * up in the target tree */ + continue; + } + + /* find the fragment index in which the symbol lies */ + ret = fdt_subnode_offset_namelen(fdto, 0, frag_name, + frag_name_len); + /* not found? */ + if (ret < 0) + return -FDT_ERR_BADOVERLAY; + fragment = ret; + + /* an __overlay__ subnode must exist */ + ret = fdt_subnode_offset(fdto, fragment, "__overlay__"); + if (ret < 0) + return -FDT_ERR_BADOVERLAY; + + /* get the target of the fragment */ + ret = fdt_overlay_target_offset(fdt, fdto, fragment, &target_path); + if (ret < 0) + return ret; + target = ret; + + /* if we have a target path use */ + if (!target_path) { + ret = get_path_len(fdt, target); + if (ret < 0) + return ret; + len = ret; + } else { + len = strlen(target_path); + } + + ret = fdt_setprop_placeholder(fdt, root_sym, name, + len + (len > 1) + rel_path_len + 1, &p); + if (ret < 0) + return ret; + + if (!target_path) { + /* again in case setprop_placeholder changed it */ + ret = fdt_overlay_target_offset(fdt, fdto, fragment, &target_path); + if (ret < 0) + return ret; + target = ret; + } + + buf = p; + if (len > 1) { /* target is not root */ + if (!target_path) { + ret = fdt_get_path(fdt, target, buf, len + 1); + if (ret < 0) + return ret; + } else + memcpy(buf, target_path, len + 1); + + } else + len--; + + buf[len] = '/'; + memcpy(buf + len + 1, rel_path, rel_path_len); + buf[len + 1 + rel_path_len] = '\0'; + } + + return 0; +} + +int fdt_overlay_apply(void *fdt, void *fdto) +{ + uint32_t delta; + int ret; + + FDT_RO_PROBE(fdt); + FDT_RO_PROBE(fdto); + + ret = fdt_find_max_phandle(fdt, &delta); + if (ret) + goto err; + + ret = overlay_adjust_local_phandles(fdto, delta); + if (ret) + goto err; + + ret = overlay_update_local_references(fdto, delta); + if (ret) + goto err; + + ret = overlay_fixup_phandles(fdt, fdto); + if (ret) + goto err; + + ret = overlay_merge(fdt, fdto); + if (ret) + goto err; + + ret = overlay_symbol_update(fdt, fdto); + if (ret) + goto err; + + /* + * The overlay has been damaged, erase its magic. + */ + fdt_set_magic(fdto, ~0); + + return 0; + +err: + /* + * The overlay might have been damaged, erase its magic. + */ + fdt_set_magic(fdto, ~0); + + /* + * The base device tree might have been damaged, erase its + * magic. + */ + fdt_set_magic(fdt, ~0); + + return ret; +} diff --git a/components/drivers/ofw/libfdt/fdt_ro.c b/components/drivers/ofw/libfdt/fdt_ro.c new file mode 100644 index 0000000000..9f6c551a22 --- /dev/null +++ b/components/drivers/ofw/libfdt/fdt_ro.c @@ -0,0 +1,859 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +static int fdt_nodename_eq_(const void *fdt, int offset, + const char *s, int len) +{ + int olen; + const char *p = fdt_get_name(fdt, offset, &olen); + + if (!p || olen < len) + /* 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_get_string(const void *fdt, int stroffset, int *lenp) +{ + int32_t totalsize; + uint32_t absoffset; + size_t len; + int err; + const char *s, *n; + + if (can_assume(VALID_INPUT)) { + s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; + + if (lenp) + *lenp = strlen(s); + return s; + } + totalsize = fdt_ro_probe_(fdt); + err = totalsize; + if (totalsize < 0) + goto fail; + + err = -FDT_ERR_BADOFFSET; + absoffset = stroffset + fdt_off_dt_strings(fdt); + if (absoffset >= (unsigned)totalsize) + goto fail; + len = totalsize - absoffset; + + if (fdt_magic(fdt) == FDT_MAGIC) { + if (stroffset < 0) + goto fail; + if (can_assume(LATEST) || fdt_version(fdt) >= 17) { + if ((unsigned)stroffset >= fdt_size_dt_strings(fdt)) + goto fail; + if ((fdt_size_dt_strings(fdt) - stroffset) < len) + len = fdt_size_dt_strings(fdt) - stroffset; + } + } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { + unsigned int sw_stroffset = -stroffset; + + if ((stroffset >= 0) || + (sw_stroffset > fdt_size_dt_strings(fdt))) + goto fail; + if (sw_stroffset < len) + len = sw_stroffset; + } else { + err = -FDT_ERR_INTERNAL; + goto fail; + } + + s = (const char *)fdt + absoffset; + n = memchr(s, '\0', len); + if (!n) { + /* missing terminating NULL */ + err = -FDT_ERR_TRUNCATED; + goto fail; + } + + if (lenp) + *lenp = n - s; + return s; + +fail: + if (lenp) + *lenp = err; + return NULL; +} + +const char *fdt_string(const void *fdt, int stroffset) +{ + return fdt_get_string(fdt, stroffset, NULL); +} + +static int fdt_string_eq_(const void *fdt, int stroffset, + const char *s, int len) +{ + int slen; + const char *p = fdt_get_string(fdt, stroffset, &slen); + + return p && (slen == len) && (memcmp(p, s, len) == 0); +} + +int fdt_find_max_phandle(const void *fdt, uint32_t *phandle) +{ + uint32_t max = 0; + int offset = -1; + + while (true) { + uint32_t value; + + offset = fdt_next_node(fdt, offset, NULL); + if (offset < 0) { + if (offset == -FDT_ERR_NOTFOUND) + break; + + return offset; + } + + value = fdt_get_phandle(fdt, offset); + + if (value > max) + max = value; + } + + if (phandle) + *phandle = max; + + return 0; +} + +int fdt_generate_phandle(const void *fdt, uint32_t *phandle) +{ + uint32_t max; + int err; + + err = fdt_find_max_phandle(fdt, &max); + if (err < 0) + return err; + + if (max == FDT_MAX_PHANDLE) + return -FDT_ERR_NOPHANDLES; + + if (phandle) + *phandle = max + 1; + + return 0; +} + +static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n) +{ + unsigned int offset = n * sizeof(struct fdt_reserve_entry); + unsigned int absoffset = fdt_off_mem_rsvmap(fdt) + offset; + + if (!can_assume(VALID_INPUT)) { + if (absoffset < fdt_off_mem_rsvmap(fdt)) + return NULL; + if (absoffset > fdt_totalsize(fdt) - + sizeof(struct fdt_reserve_entry)) + return NULL; + } + return fdt_mem_rsv_(fdt, n); +} + +int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) +{ + const struct fdt_reserve_entry *re; + + FDT_RO_PROBE(fdt); + re = fdt_mem_rsv(fdt, n); + if (!can_assume(VALID_INPUT) && !re) + return -FDT_ERR_BADOFFSET; + + *address = fdt64_ld_(&re->address); + *size = fdt64_ld_(&re->size); + return 0; +} + +int fdt_num_mem_rsv(const void *fdt) +{ + int i; + const struct fdt_reserve_entry *re; + + for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) { + if (fdt64_ld_(&re->size) == 0) + return i; + } + return -FDT_ERR_TRUNCATED; +} + +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_RO_PROBE(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_RO_PROBE(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); + const char *nameptr; + int err; + + if (((err = fdt_ro_probe_(fdt)) < 0) + || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)) + goto fail; + + nameptr = nh->name; + + if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { + /* + * For old FDT versions, match the naming conventions of V16: + * give only the leaf name (after all /). The actual tree + * contents are loosely checked. + */ + const char *leaf; + leaf = strrchr(nameptr, '/'); + if (leaf == NULL) { + err = -FDT_ERR_BADSTRUCTURE; + goto fail; + } + nameptr = leaf+1; + } + + if (len) + *len = strlen(nameptr); + + return nameptr; + + 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); +} + +static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt, + int offset, + int *lenp) +{ + int err; + const struct fdt_property *prop; + + if (!can_assume(VALID_INPUT) && + (err = fdt_check_prop_offset_(fdt, offset)) < 0) { + if (lenp) + *lenp = err; + return NULL; + } + + prop = fdt_offset_ptr_(fdt, offset); + + if (lenp) + *lenp = fdt32_ld_(&prop->len); + + return prop; +} + +const struct fdt_property *fdt_get_property_by_offset(const void *fdt, + int offset, + int *lenp) +{ + /* Prior to version 16, properties may need realignment + * and this API does not work. fdt_getprop_*() will, however. */ + + if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { + if (lenp) + *lenp = -FDT_ERR_BADVERSION; + return NULL; + } + + return fdt_get_property_by_offset_(fdt, offset, lenp); +} + +static const struct fdt_property *fdt_get_property_namelen_(const void *fdt, + int offset, + const char *name, + int namelen, + int *lenp, + int *poffset) +{ + for (offset = fdt_first_property_offset(fdt, offset); + (offset >= 0); + (offset = fdt_next_property_offset(fdt, offset))) { + const struct fdt_property *prop; + + prop = fdt_get_property_by_offset_(fdt, offset, lenp); + if (!can_assume(LIBFDT_FLAWLESS) && !prop) { + offset = -FDT_ERR_INTERNAL; + break; + } + if (fdt_string_eq_(fdt, fdt32_ld_(&prop->nameoff), + name, namelen)) { + if (poffset) + *poffset = offset; + return prop; + } + } + + if (lenp) + *lenp = offset; + return NULL; +} + + +const struct fdt_property *fdt_get_property_namelen(const void *fdt, + int offset, + const char *name, + int namelen, int *lenp) +{ + /* Prior to version 16, properties may need realignment + * and this API does not work. fdt_getprop_*() will, however. */ + if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { + if (lenp) + *lenp = -FDT_ERR_BADVERSION; + return NULL; + } + + return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp, + 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) +{ + int poffset; + const struct fdt_property *prop; + + prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp, + &poffset); + if (!prop) + return NULL; + + /* Handle realignment */ + if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 && + (poffset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8) + return prop->data + 4; + 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) { + const char *name; + int namelen; + + if (!can_assume(VALID_INPUT)) { + name = fdt_get_string(fdt, fdt32_ld_(&prop->nameoff), + &namelen); + *namep = name; + if (!name) { + if (lenp) + *lenp = namelen; + return NULL; + } + } else { + *namep = fdt_string(fdt, fdt32_ld_(&prop->nameoff)); + } + } + + /* Handle realignment */ + if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 && + (offset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8) + return prop->data + 4; + 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_ld_(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_RO_PROBE(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_RO_PROBE(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 (!can_assume(VALID_INPUT)) { + 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 (can_assume(LIBFDT_FLAWLESS) || 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_RO_PROBE(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 == ~0U)) + return -FDT_ERR_BADPHANDLE; + + FDT_RO_PROBE(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_RO_PROBE(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() */ +} diff --git a/components/drivers/ofw/libfdt/fdt_rw.c b/components/drivers/ofw/libfdt/fdt_rw.c new file mode 100644 index 0000000000..3621d3651d --- /dev/null +++ b/components/drivers/ofw/libfdt/fdt_rw.c @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include +#include + +#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_probe_(void *fdt) +{ + if (can_assume(VALID_DTB)) + return 0; + FDT_RO_PROBE(fdt); + + if (!can_assume(LATEST) && 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 (!can_assume(LATEST) && fdt_version(fdt) > 17) + fdt_set_version(fdt, 17); + + return 0; +} + +#define FDT_RW_PROBE(fdt) \ + { \ + int err_; \ + if ((err_ = fdt_rw_probe_(fdt)) != 0) \ + return err_; \ + } + +static inline unsigned 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; + unsigned int dsize = fdt_data_size_(fdt); + size_t soff = p - (char *)fdt; + + if ((oldlen < 0) || (soff + oldlen < soff) || (soff + oldlen > dsize)) + return -FDT_ERR_BADOFFSET; + if ((p < (char *)fdt) || (dsize + newlen < (unsigned)oldlen)) + return -FDT_ERR_BADOFFSET; + if (dsize - oldlen + newlen > fdt_totalsize(fdt)) + return -FDT_ERR_NOSPACE; + memmove(p + newlen, p + oldlen, ((char *)fdt + dsize) - (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; +} + +/* Must only be used to roll back in case of error */ +static void fdt_del_last_string_(void *fdt, const char *s) +{ + int newlen = strlen(s) + 1; + + fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) - newlen); +} + +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; +} + +/** + * fdt_find_add_string_() - Find or allocate a string + * + * @fdt: pointer to the device tree to check/adjust + * @s: string to find/add + * @allocated: Set to 0 if the string was found, 1 if not found and so + * allocated. Ignored if can_assume(NO_ROLLBACK) + * @return offset of string in the string table (whether found or added) + */ +static int fdt_find_add_string_(void *fdt, const char *s, int *allocated) +{ + char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); + const char *p; + char *new; + int len = strlen(s) + 1; + int err; + + if (!can_assume(NO_ROLLBACK)) + *allocated = 0; + + 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; + + if (!can_assume(NO_ROLLBACK)) + *allocated = 1; + + 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_PROBE(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_PROBE(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; + int allocated; + + if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) + return nextoffset; + + namestroff = fdt_find_add_string_(fdt, name, &allocated); + 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) { + /* Delete the string if we failed to add it */ + if (!can_assume(NO_ROLLBACK) && allocated) + fdt_del_last_string_(fdt, name); + 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_PROBE(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_placeholder(void *fdt, int nodeoffset, const char *name, + int len, void **prop_data) +{ + struct fdt_property *prop; + int err; + + FDT_RW_PROBE(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; + + *prop_data = prop->data; + return 0; +} + +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + void *prop_data; + int err; + + err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data); + 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_PROBE(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_PROBE(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_PROBE(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 */ + tag = fdt_next_tag(fdt, parentoffset, &nextoffset); + /* the fdt_subnode_offset_namelen() should ensure this never hits */ + if (!can_assume(LIBFDT_FLAWLESS) && (tag != FDT_BEGIN_NODE)) + return -FDT_ERR_INTERNAL; + 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_PROBE(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 strings_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), strings_size); + 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_RO_PROBE(fdt); + + mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) + * sizeof(struct fdt_reserve_entry); + + if (can_assume(LATEST) || fdt_version(fdt) >= 17) { + struct_size = fdt_size_dt_struct(fdt); + } else if (fdt_version(fdt) == 16) { + struct_size = 0; + while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END) + ; + if (struct_size < 0) + return struct_size; + } else { + return -FDT_ERR_BADVERSION; + } + + if (can_assume(LIBFDT_ORDER) || + !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, + fdt_size_dt_strings(fdt)); + 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_PROBE(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_size_dt_strings(fdt)); + fdt_set_totalsize(fdt, fdt_data_size_(fdt)); + + return 0; +} diff --git a/components/drivers/ofw/libfdt/fdt_strerror.c b/components/drivers/ofw/libfdt/fdt_strerror.c new file mode 100644 index 0000000000..d852b77e81 --- /dev/null +++ b/components/drivers/ofw/libfdt/fdt_strerror.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" + +#include +#include + +#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), + FDT_ERRTABENT(FDT_ERR_BADFLAGS), + FDT_ERRTABENT(FDT_ERR_ALIGNMENT), +}; +#define FDT_ERRTABSIZE ((int)(sizeof(fdt_errtable) / sizeof(fdt_errtable[0]))) + +const char *fdt_strerror(int errval) +{ + if (errval > 0) + return ""; + else if (errval == 0) + return ""; + else if (-errval < FDT_ERRTABSIZE) { + const char *s = fdt_errtable[-errval].str; + + if (s) + return s; + } + + return ""; +} diff --git a/components/drivers/ofw/libfdt/fdt_sw.c b/components/drivers/ofw/libfdt/fdt_sw.c new file mode 100644 index 0000000000..4c569ee7eb --- /dev/null +++ b/components/drivers/ofw/libfdt/fdt_sw.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +static int fdt_sw_probe_(void *fdt) +{ + if (!can_assume(VALID_INPUT)) { + if (fdt_magic(fdt) == FDT_MAGIC) + return -FDT_ERR_BADSTATE; + else if (fdt_magic(fdt) != FDT_SW_MAGIC) + return -FDT_ERR_BADMAGIC; + } + + return 0; +} + +#define FDT_SW_PROBE(fdt) \ + { \ + int err; \ + if ((err = fdt_sw_probe_(fdt)) != 0) \ + return err; \ + } + +/* 'memrsv' state: Initial state after fdt_create() + * + * Allowed functions: + * fdt_add_reservemap_entry() + * fdt_finish_reservemap() [moves to 'struct' state] + */ +static int fdt_sw_probe_memrsv_(void *fdt) +{ + int err = fdt_sw_probe_(fdt); + if (err) + return err; + + if (!can_assume(VALID_INPUT) && fdt_off_dt_strings(fdt) != 0) + return -FDT_ERR_BADSTATE; + return 0; +} + +#define FDT_SW_PROBE_MEMRSV(fdt) \ + { \ + int err; \ + if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \ + return err; \ + } + +/* 'struct' state: Enter this state after fdt_finish_reservemap() + * + * Allowed functions: + * fdt_begin_node() + * fdt_end_node() + * fdt_property*() + * fdt_finish() [moves to 'complete' state] + */ +static int fdt_sw_probe_struct_(void *fdt) +{ + int err = fdt_sw_probe_(fdt); + if (err) + return err; + + if (!can_assume(VALID_INPUT) && + fdt_off_dt_strings(fdt) != fdt_totalsize(fdt)) + return -FDT_ERR_BADSTATE; + return 0; +} + +#define FDT_SW_PROBE_STRUCT(fdt) \ + { \ + int err; \ + if ((err = fdt_sw_probe_struct_(fdt)) != 0) \ + return err; \ + } + +static inline uint32_t sw_flags(void *fdt) +{ + /* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */ + return fdt_last_comp_version(fdt); +} + +/* 'complete' state: Enter this state after fdt_finish() + * + * Allowed functions: none + */ + +static void *fdt_grab_space_(void *fdt, size_t len) +{ + unsigned int offset = fdt_size_dt_struct(fdt); + unsigned 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_with_flags(void *buf, int bufsize, uint32_t flags) +{ + const int hdrsize = FDT_ALIGN(sizeof(struct fdt_header), + sizeof(struct fdt_reserve_entry)); + void *fdt = buf; + + if (bufsize < hdrsize) + return -FDT_ERR_NOSPACE; + + if (flags & ~FDT_CREATE_FLAGS_ALL) + return -FDT_ERR_BADFLAGS; + + memset(buf, 0, bufsize); + + /* + * magic and last_comp_version keep intermediate state during the fdt + * creation process, which is replaced with the proper FDT format by + * fdt_finish(). + * + * flags should be accessed with sw_flags(). + */ + fdt_set_magic(fdt, FDT_SW_MAGIC); + fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); + fdt_set_last_comp_version(fdt, flags); + + fdt_set_totalsize(fdt, bufsize); + + fdt_set_off_mem_rsvmap(fdt, hdrsize); + fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); + fdt_set_off_dt_strings(fdt, 0); + + return 0; +} + +int fdt_create(void *buf, int bufsize) +{ + return fdt_create_with_flags(buf, bufsize, 0); +} + +int fdt_resize(void *fdt, void *buf, int bufsize) +{ + size_t headsize, tailsize; + char *oldtail, *newtail; + + FDT_SW_PROBE(fdt); + + if (bufsize < 0) + return -FDT_ERR_NOSPACE; + + headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); + tailsize = fdt_size_dt_strings(fdt); + + if (!can_assume(VALID_DTB) && + headsize + tailsize > fdt_totalsize(fdt)) + return -FDT_ERR_INTERNAL; + + if ((headsize + tailsize) > (unsigned)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_totalsize(buf, bufsize); + if (fdt_off_dt_strings(buf)) + fdt_set_off_dt_strings(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_PROBE_MEMRSV(fdt); + + 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) +{ + int err = fdt_add_reservemap_entry(fdt, 0, 0); + + if (err) + return err; + + fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt)); + return 0; +} + +int fdt_begin_node(void *fdt, const char *name) +{ + struct fdt_node_header *nh; + int namelen; + + FDT_SW_PROBE_STRUCT(fdt); + + namelen = strlen(name) + 1; + 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_PROBE_STRUCT(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_add_string_(void *fdt, const char *s) +{ + char *strtab = (char *)fdt + fdt_totalsize(fdt); + unsigned int strtabsize = fdt_size_dt_strings(fdt); + unsigned int len = strlen(s) + 1; + unsigned int struct_top, offset; + + 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; +} + +/* Must only be used to roll back in case of error */ +static void fdt_del_last_string_(void *fdt, const char *s) +{ + int strtabsize = fdt_size_dt_strings(fdt); + int len = strlen(s) + 1; + + fdt_set_size_dt_strings(fdt, strtabsize - len); +} + +static int fdt_find_add_string_(void *fdt, const char *s, int *allocated) +{ + char *strtab = (char *)fdt + fdt_totalsize(fdt); + int strtabsize = fdt_size_dt_strings(fdt); + const char *p; + + *allocated = 0; + + p = fdt_find_string_(strtab - strtabsize, strtabsize, s); + if (p) + return p - strtab; + + *allocated = 1; + + return fdt_add_string_(fdt, s); +} + +int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp) +{ + struct fdt_property *prop; + int nameoff; + int allocated; + + FDT_SW_PROBE_STRUCT(fdt); + + /* String de-duplication can be slow, _NO_NAME_DEDUP skips it */ + if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) { + allocated = 1; + nameoff = fdt_add_string_(fdt, name); + } else { + nameoff = fdt_find_add_string_(fdt, name, &allocated); + } + if (nameoff == 0) + return -FDT_ERR_NOSPACE; + + prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); + if (! prop) { + if (allocated) + fdt_del_last_string_(fdt, name); + return -FDT_ERR_NOSPACE; + } + + prop->tag = cpu_to_fdt32(FDT_PROP); + prop->nameoff = cpu_to_fdt32(nameoff); + prop->len = cpu_to_fdt32(len); + *valp = prop->data; + return 0; +} + +int fdt_property(void *fdt, const char *name, const void *val, int len) +{ + void *ptr; + int ret; + + ret = fdt_property_placeholder(fdt, name, len, &ptr); + if (ret) + return ret; + memcpy(ptr, 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_PROBE_STRUCT(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)); + + /* And fix up fields that were keeping intermediate state. */ + fdt_set_last_comp_version(fdt, FDT_LAST_COMPATIBLE_VERSION); + fdt_set_magic(fdt, FDT_MAGIC); + + return 0; +} diff --git a/components/drivers/ofw/libfdt/fdt_wip.c b/components/drivers/ofw/libfdt/fdt_wip.c new file mode 100644 index 0000000000..c2d7566a67 --- /dev/null +++ b/components/drivers/ofw/libfdt/fdt_wip.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include +#include + +#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 ((unsigned)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; +} diff --git a/components/drivers/ofw/libfdt/libfdt.h b/components/drivers/ofw/libfdt/libfdt.h new file mode 100644 index 0000000000..77ccff1991 --- /dev/null +++ b/components/drivers/ofw/libfdt/libfdt.h @@ -0,0 +1,2154 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ +#ifndef LIBFDT_H +#define LIBFDT_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ + +#include "libfdt_env.h" +#include "fdt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define FDT_FIRST_SUPPORTED_VERSION 0x02 +#define FDT_LAST_COMPATIBLE_VERSION 0x10 +#define FDT_LAST_SUPPORTED_VERSION 0x11 + +/* Error codes: informative error codes */ +#define FDT_ERR_NOTFOUND 1 + /* FDT_ERR_NOTFOUND: The requested node or property does not exist */ +#define FDT_ERR_EXISTS 2 + /* FDT_ERR_EXISTS: Attempted to create a node or property which + * already exists */ +#define FDT_ERR_NOSPACE 3 + /* FDT_ERR_NOSPACE: Operation needed to expand the device + * tree, but its buffer did not have sufficient space to + * contain the expanded tree. Use fdt_open_into() to move the + * device tree to a buffer with more space. */ + +/* Error codes: codes for bad parameters */ +#define FDT_ERR_BADOFFSET 4 + /* FDT_ERR_BADOFFSET: Function was passed a structure block + * offset which is out-of-bounds, or which points to an + * unsuitable part of the structure for the operation. */ +#define FDT_ERR_BADPATH 5 + /* FDT_ERR_BADPATH: Function was passed a badly formatted path + * (e.g. missing a leading / for a function which requires an + * absolute path) */ +#define FDT_ERR_BADPHANDLE 6 + /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle. + * This can be caused either by an invalid phandle property + * length, or the phandle value was either 0 or -1, which are + * not permitted. */ +#define FDT_ERR_BADSTATE 7 + /* FDT_ERR_BADSTATE: Function was passed an incomplete device + * tree created by the sequential-write functions, which is + * not sufficiently complete for the requested operation. */ + +/* Error codes: codes for bad device tree blobs */ +#define FDT_ERR_TRUNCATED 8 + /* FDT_ERR_TRUNCATED: FDT or a sub-block is improperly + * terminated (overflows, goes outside allowed bounds, or + * isn't properly terminated). */ +#define FDT_ERR_BADMAGIC 9 + /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a + * device tree at all - it is missing the flattened device + * tree magic number. */ +#define FDT_ERR_BADVERSION 10 + /* FDT_ERR_BADVERSION: Given device tree has a version which + * can't be handled by the requested operation. For + * read-write functions, this may mean that fdt_open_into() is + * required to convert the tree to the expected version. */ +#define FDT_ERR_BADSTRUCTURE 11 + /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt + * structure block or other serious error (e.g. misnested + * nodes, or subnodes preceding properties). */ +#define FDT_ERR_BADLAYOUT 12 + /* FDT_ERR_BADLAYOUT: For read-write functions, the given + * device tree has it's sub-blocks in an order that the + * function can't handle (memory reserve map, then structure, + * then strings). Use fdt_open_into() to reorganize the tree + * into a form suitable for the read-write operations. */ + +/* "Can't happen" error indicating a bug in libfdt */ +#define FDT_ERR_INTERNAL 13 + /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion. + * Should never be returned, if it is, it indicates a bug in + * libfdt itself. */ + +/* Errors in device tree content */ +#define FDT_ERR_BADNCELLS 14 + /* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells + * or similar property with a bad format or value */ + +#define FDT_ERR_BADVALUE 15 + /* FDT_ERR_BADVALUE: Device tree has a property with an unexpected + * value. For example: a property expected to contain a string list + * is not NUL-terminated within the length of its value. */ + +#define FDT_ERR_BADOVERLAY 16 + /* FDT_ERR_BADOVERLAY: The device tree overlay, while + * correctly structured, cannot be applied due to some + * unexpected or missing value, property or node. */ + +#define FDT_ERR_NOPHANDLES 17 + /* FDT_ERR_NOPHANDLES: The device tree doesn't have any + * phandle available anymore without causing an overflow */ + +#define FDT_ERR_BADFLAGS 18 + /* FDT_ERR_BADFLAGS: The function was passed a flags field that + * contains invalid flags or an invalid combination of flags. */ + +#define FDT_ERR_ALIGNMENT 19 + /* FDT_ERR_ALIGNMENT: The device tree base address is not 8-byte + * aligned. */ + +#define FDT_ERR_MAX 19 + +/* constants */ +#define FDT_MAX_PHANDLE 0xfffffffe + /* Valid values for phandles range from 1 to 2^32-2. */ + +/**********************************************************************/ +/* Low-level functions (you probably don't need these) */ +/**********************************************************************/ + +#ifndef SWIG /* This function is not useful in Python */ +const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen); +#endif +static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen) +{ + return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen); +} + +uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset); + +/* + * External helpers to access words from a device tree blob. They're built + * to work even with unaligned pointers on platforms (such as ARMv5) that don't + * like unaligned loads and stores. + */ +static inline uint16_t fdt16_ld(const fdt16_t *p) +{ + const uint8_t *bp = (const uint8_t *)p; + + return ((uint16_t)bp[0] << 8) | bp[1]; +} + +static inline uint32_t fdt32_ld(const fdt32_t *p) +{ + const uint8_t *bp = (const uint8_t *)p; + + return ((uint32_t)bp[0] << 24) + | ((uint32_t)bp[1] << 16) + | ((uint32_t)bp[2] << 8) + | bp[3]; +} + +static inline void fdt32_st(void *property, uint32_t value) +{ + uint8_t *bp = (uint8_t *)property; + + bp[0] = value >> 24; + bp[1] = (value >> 16) & 0xff; + bp[2] = (value >> 8) & 0xff; + bp[3] = value & 0xff; +} + +static inline uint64_t fdt64_ld(const fdt64_t *p) +{ + const uint8_t *bp = (const uint8_t *)p; + + return ((uint64_t)bp[0] << 56) + | ((uint64_t)bp[1] << 48) + | ((uint64_t)bp[2] << 40) + | ((uint64_t)bp[3] << 32) + | ((uint64_t)bp[4] << 24) + | ((uint64_t)bp[5] << 16) + | ((uint64_t)bp[6] << 8) + | bp[7]; +} + +static inline void fdt64_st(void *property, uint64_t value) +{ + uint8_t *bp = (uint8_t *)property; + + bp[0] = value >> 56; + bp[1] = (value >> 48) & 0xff; + bp[2] = (value >> 40) & 0xff; + bp[3] = (value >> 32) & 0xff; + bp[4] = (value >> 24) & 0xff; + bp[5] = (value >> 16) & 0xff; + bp[6] = (value >> 8) & 0xff; + bp[7] = value & 0xff; +} + +/**********************************************************************/ +/* Traversal functions */ +/**********************************************************************/ + +int fdt_next_node(const void *fdt, int offset, int *depth); + +/** + * fdt_first_subnode() - get offset of first direct subnode + * @fdt: FDT blob + * @offset: Offset of node to check + * + * Return: offset of first subnode, or -FDT_ERR_NOTFOUND if there is none + */ +int fdt_first_subnode(const void *fdt, int offset); + +/** + * fdt_next_subnode() - get offset of next direct subnode + * @fdt: FDT blob + * @offset: Offset of previous subnode + * + * After first calling fdt_first_subnode(), call this function repeatedly to + * get direct subnodes of a parent node. + * + * Return: offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more + * subnodes + */ +int fdt_next_subnode(const void *fdt, int offset); + +/** + * fdt_for_each_subnode - iterate over all subnodes of a parent + * + * @node: child node (int, lvalue) + * @fdt: FDT blob (const void *) + * @parent: parent node (int) + * + * This is actually a wrapper around a for loop and would be used like so: + * + * fdt_for_each_subnode(node, fdt, parent) { + * Use node + * ... + * } + * + * if ((node < 0) && (node != -FDT_ERR_NOTFOUND)) { + * Error handling + * } + * + * Note that this is implemented as a macro and @node is used as + * iterator in the loop. The parent variable be constant or even a + * literal. + */ +#define fdt_for_each_subnode(node, fdt, parent) \ + for (node = fdt_first_subnode(fdt, parent); \ + node >= 0; \ + node = fdt_next_subnode(fdt, node)) + +/**********************************************************************/ +/* General functions */ +/**********************************************************************/ +#define fdt_get_header(fdt, field) \ + (fdt32_ld(&((const struct fdt_header *)(fdt))->field)) +#define fdt_magic(fdt) (fdt_get_header(fdt, magic)) +#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize)) +#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct)) +#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings)) +#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap)) +#define fdt_version(fdt) (fdt_get_header(fdt, version)) +#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version)) +#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys)) +#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings)) +#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct)) + +#define fdt_set_hdr_(name) \ + static inline void fdt_set_##name(void *fdt, uint32_t val) \ + { \ + struct fdt_header *fdth = (struct fdt_header *)fdt; \ + fdth->name = cpu_to_fdt32(val); \ + } +fdt_set_hdr_(magic); +fdt_set_hdr_(totalsize); +fdt_set_hdr_(off_dt_struct); +fdt_set_hdr_(off_dt_strings); +fdt_set_hdr_(off_mem_rsvmap); +fdt_set_hdr_(version); +fdt_set_hdr_(last_comp_version); +fdt_set_hdr_(boot_cpuid_phys); +fdt_set_hdr_(size_dt_strings); +fdt_set_hdr_(size_dt_struct); +#undef fdt_set_hdr_ + +/** + * fdt_header_size - return the size of the tree's header + * @fdt: pointer to a flattened device tree + * + * Return: size of DTB header in bytes + */ +size_t fdt_header_size(const void *fdt); + +/** + * fdt_header_size_ - internal function to get header size from a version number + * @version: devicetree version number + * + * Return: size of DTB header in bytes + */ +size_t fdt_header_size_(uint32_t version); + +/** + * fdt_check_header - sanity check a device tree header + * @fdt: pointer to data which might be a flattened device tree + * + * fdt_check_header() checks that the given buffer contains what + * appears to be a flattened device tree, and that the header contains + * valid information (to the extent that can be determined from the + * header alone). + * + * returns: + * 0, if the buffer appears to contain a valid device tree + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_TRUNCATED, standard meanings, as above + */ +int fdt_check_header(const void *fdt); + +/** + * fdt_move - move a device tree around in memory + * @fdt: pointer to the device tree to move + * @buf: pointer to memory where the device is to be moved + * @bufsize: size of the memory space at buf + * + * fdt_move() relocates, if possible, the device tree blob located at + * fdt to the buffer at buf of size bufsize. The buffer may overlap + * with the existing device tree blob at fdt. Therefore, + * fdt_move(fdt, fdt, fdt_totalsize(fdt)) + * should always succeed. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_move(const void *fdt, void *buf, int bufsize); + +/**********************************************************************/ +/* Read-only functions */ +/**********************************************************************/ + +int fdt_check_full(const void *fdt, size_t bufsize); + +/** + * fdt_get_string - retrieve a string from the strings block of a device tree + * @fdt: pointer to the device tree blob + * @stroffset: offset of the string within the strings block (native endian) + * @lenp: optional pointer to return the string's length + * + * fdt_get_string() retrieves a pointer to a single string from the + * strings block of the device tree blob at fdt, and optionally also + * returns the string's length in *lenp. + * + * returns: + * a pointer to the string, on success + * NULL, if stroffset is out of bounds, or doesn't point to a valid string + */ +const char *fdt_get_string(const void *fdt, int stroffset, int *lenp); + +/** + * fdt_string - retrieve a string from the strings block of a device tree + * @fdt: pointer to the device tree blob + * @stroffset: offset of the string within the strings block (native endian) + * + * fdt_string() retrieves a pointer to a single string from the + * strings block of the device tree blob at fdt. + * + * returns: + * a pointer to the string, on success + * NULL, if stroffset is out of bounds, or doesn't point to a valid string + */ +const char *fdt_string(const void *fdt, int stroffset); + +/** + * fdt_find_max_phandle - find and return the highest phandle in a tree + * @fdt: pointer to the device tree blob + * @phandle: return location for the highest phandle value found in the tree + * + * fdt_find_max_phandle() finds the highest phandle value in the given device + * tree. The value returned in @phandle is only valid if the function returns + * success. + * + * returns: + * 0 on success or a negative error code on failure + */ +int fdt_find_max_phandle(const void *fdt, uint32_t *phandle); + +/** + * fdt_get_max_phandle - retrieves the highest phandle in a tree + * @fdt: pointer to the device tree blob + * + * fdt_get_max_phandle retrieves the highest phandle in the given + * device tree. This will ignore badly formatted phandles, or phandles + * with a value of 0 or -1. + * + * This function is deprecated in favour of fdt_find_max_phandle(). + * + * returns: + * the highest phandle on success + * 0, if no phandle was found in the device tree + * -1, if an error occurred + */ +static inline uint32_t fdt_get_max_phandle(const void *fdt) +{ + uint32_t phandle; + int err; + + err = fdt_find_max_phandle(fdt, &phandle); + if (err < 0) + return (uint32_t)-1; + + return phandle; +} + +/** + * fdt_generate_phandle - return a new, unused phandle for a device tree blob + * @fdt: pointer to the device tree blob + * @phandle: return location for the new phandle + * + * Walks the device tree blob and looks for the highest phandle value. On + * success, the new, unused phandle value (one higher than the previously + * highest phandle value in the device tree blob) will be returned in the + * @phandle parameter. + * + * Return: 0 on success or a negative error-code on failure + */ +int fdt_generate_phandle(const void *fdt, uint32_t *phandle); + +/** + * fdt_num_mem_rsv - retrieve the number of memory reserve map entries + * @fdt: pointer to the device tree blob + * + * Returns the number of entries in the device tree blob's memory + * reservation map. This does not include the terminating 0,0 entry + * or any other (0,0) entries reserved for expansion. + * + * returns: + * the number of entries + */ +int fdt_num_mem_rsv(const void *fdt); + +/** + * fdt_get_mem_rsv - retrieve one memory reserve map entry + * @fdt: pointer to the device tree blob + * @n: index of reserve map entry + * @address: pointer to 64-bit variable to hold the start address + * @size: pointer to 64-bit variable to hold the size of the entry + * + * On success, @address and @size will contain the address and size of + * the n-th reserve map entry from the device tree blob, in + * native-endian format. + * + * returns: + * 0, on success + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size); + +/** + * fdt_subnode_offset_namelen - find a subnode based on substring + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * @namelen: number of characters of name to consider + * + * Identical to fdt_subnode_offset(), but only examine the first + * namelen characters of name for matching the subnode name. This is + * useful for finding subnodes based on a portion of a larger string, + * such as a full path. + * + * Return: offset of the subnode or -FDT_ERR_NOTFOUND if name not found. + */ +#ifndef SWIG /* Not available in Python */ +int fdt_subnode_offset_namelen(const void *fdt, int parentoffset, + const char *name, int namelen); +#endif +/** + * fdt_subnode_offset - find a subnode of a given node + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * + * fdt_subnode_offset() finds a subnode of the node at structure block + * offset parentoffset with the given name. name may include a unit + * address, in which case fdt_subnode_offset() will find the subnode + * with that unit address, or the unit address may be omitted, in + * which case fdt_subnode_offset() will find an arbitrary subnode + * whose name excluding unit address matches the given name. + * + * returns: + * structure block offset of the requested subnode (>=0), on success + * -FDT_ERR_NOTFOUND, if the requested subnode does not exist + * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE + * tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name); + +/** + * fdt_path_offset_namelen - find a tree node by its full path + * @fdt: pointer to the device tree blob + * @path: full path of the node to locate + * @namelen: number of characters of path to consider + * + * Identical to fdt_path_offset(), but only consider the first namelen + * characters of path as the path name. + * + * Return: offset of the node or negative libfdt error value otherwise + */ +#ifndef SWIG /* Not available in Python */ +int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen); +#endif + +/** + * fdt_path_offset - find a tree node by its full path + * @fdt: pointer to the device tree blob + * @path: full path of the node to locate + * + * fdt_path_offset() finds a node of a given path in the device tree. + * Each path component may omit the unit address portion, but the + * results of this are undefined if any such path component is + * ambiguous (that is if there are multiple nodes at the relevant + * level matching the given component, differentiated only by unit + * address). + * + * returns: + * structure block offset of the node with the requested path (>=0), on + * success + * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid + * -FDT_ERR_NOTFOUND, if the requested node does not exist + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_path_offset(const void *fdt, const char *path); + +/** + * fdt_get_name - retrieve the name of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of the starting node + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_name() retrieves the name (including unit address) of the + * device tree node at structure block offset nodeoffset. If lenp is + * non-NULL, the length of this name is also returned, in the integer + * pointed to by lenp. + * + * returns: + * pointer to the node's name, on success + * If lenp is non-NULL, *lenp contains the length of that name + * (>=0) + * NULL, on error + * if lenp is non-NULL *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE + * tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp); + +/** + * fdt_first_property_offset - find the offset of a node's first property + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of a node + * + * fdt_first_property_offset() finds the first property of the node at + * the given structure block offset. + * + * returns: + * structure block offset of the property (>=0), on success + * -FDT_ERR_NOTFOUND, if the requested node has no properties + * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_first_property_offset(const void *fdt, int nodeoffset); + +/** + * fdt_next_property_offset - step through a node's properties + * @fdt: pointer to the device tree blob + * @offset: structure block offset of a property + * + * fdt_next_property_offset() finds the property immediately after the + * one at the given structure block offset. This will be a property + * of the same node as the given property. + * + * returns: + * structure block offset of the next property (>=0), on success + * -FDT_ERR_NOTFOUND, if the given property is the last in its node + * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_next_property_offset(const void *fdt, int offset); + +/** + * fdt_for_each_property_offset - iterate over all properties of a node + * + * @property: property offset (int, lvalue) + * @fdt: FDT blob (const void *) + * @node: node offset (int) + * + * This is actually a wrapper around a for loop and would be used like so: + * + * fdt_for_each_property_offset(property, fdt, node) { + * Use property + * ... + * } + * + * if ((property < 0) && (property != -FDT_ERR_NOTFOUND)) { + * Error handling + * } + * + * Note that this is implemented as a macro and property is used as + * iterator in the loop. The node variable can be constant or even a + * literal. + */ +#define fdt_for_each_property_offset(property, fdt, node) \ + for (property = fdt_first_property_offset(fdt, node); \ + property >= 0; \ + property = fdt_next_property_offset(fdt, property)) + +/** + * fdt_get_property_by_offset - retrieve the property at a given offset + * @fdt: pointer to the device tree blob + * @offset: offset of the property to retrieve + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_property_by_offset() retrieves a pointer to the + * fdt_property structure within the device tree blob at the given + * offset. If lenp is non-NULL, the length of the property value is + * also returned, in the integer pointed to by lenp. + * + * Note that this code only works on device tree versions >= 16. fdt_getprop() + * works on all versions. + * + * returns: + * pointer to the structure representing the property + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const struct fdt_property *fdt_get_property_by_offset(const void *fdt, + int offset, + int *lenp); +static inline struct fdt_property *fdt_get_property_by_offset_w(void *fdt, + int offset, + int *lenp) +{ + return (struct fdt_property *)(uintptr_t) + fdt_get_property_by_offset(fdt, offset, lenp); +} + +/** + * fdt_get_property_namelen - find a property based on substring + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @namelen: number of characters of name to consider + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * Identical to fdt_get_property(), but only examine the first namelen + * characters of name for matching the property name. + * + * Return: pointer to the structure representing the property, or NULL + * if not found + */ +#ifndef SWIG /* Not available in Python */ +const struct fdt_property *fdt_get_property_namelen(const void *fdt, + int nodeoffset, + const char *name, + int namelen, int *lenp); +#endif + +/** + * fdt_get_property - find a given property in a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_property() retrieves a pointer to the fdt_property + * structure within the device tree blob corresponding to the property + * named 'name' of the node at offset nodeoffset. If lenp is + * non-NULL, the length of the property value is also returned, in the + * integer pointed to by lenp. + * + * returns: + * pointer to the structure representing the property + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_NOTFOUND, node does not have named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE + * tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, + const char *name, int *lenp); +static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset, + const char *name, + int *lenp) +{ + return (struct fdt_property *)(uintptr_t) + fdt_get_property(fdt, nodeoffset, name, lenp); +} + +/** + * fdt_getprop_by_offset - retrieve the value of a property at a given offset + * @fdt: pointer to the device tree blob + * @offset: offset of the property to read + * @namep: pointer to a string variable (will be overwritten) or NULL + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_getprop_by_offset() retrieves a pointer to the value of the + * property at structure block offset 'offset' (this will be a pointer + * to within the device blob itself, not a copy of the value). If + * lenp is non-NULL, the length of the property value is also + * returned, in the integer pointed to by lenp. If namep is non-NULL, + * the property's namne will also be returned in the char * pointed to + * by namep (this will be a pointer to within the device tree's string + * block, not a new copy of the name). + * + * returns: + * pointer to the property's value + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * if namep is non-NULL *namep contiains a pointer to the property + * name. + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#ifndef SWIG /* This function is not useful in Python */ +const void *fdt_getprop_by_offset(const void *fdt, int offset, + const char **namep, int *lenp); +#endif + +/** + * fdt_getprop_namelen - get property value based on substring + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @namelen: number of characters of name to consider + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * Identical to fdt_getprop(), but only examine the first namelen + * characters of name for matching the property name. + * + * Return: pointer to the property's value or NULL on error + */ +#ifndef SWIG /* Not available in Python */ +const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, + const char *name, int namelen, int *lenp); +static inline void *fdt_getprop_namelen_w(void *fdt, int nodeoffset, + const char *name, int namelen, + int *lenp) +{ + return (void *)(uintptr_t)fdt_getprop_namelen(fdt, nodeoffset, name, + namelen, lenp); +} +#endif + +/** + * fdt_getprop - retrieve the value of a given property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_getprop() retrieves a pointer to the value of the property + * named @name of the node at offset @nodeoffset (this will be a + * pointer to within the device blob itself, not a copy of the value). + * If @lenp is non-NULL, the length of the property value is also + * returned, in the integer pointed to by @lenp. + * + * returns: + * pointer to the property's value + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_NOTFOUND, node does not have named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE + * tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const void *fdt_getprop(const void *fdt, int nodeoffset, + const char *name, int *lenp); +static inline void *fdt_getprop_w(void *fdt, int nodeoffset, + const char *name, int *lenp) +{ + return (void *)(uintptr_t)fdt_getprop(fdt, nodeoffset, name, lenp); +} + +/** + * fdt_get_phandle - retrieve the phandle of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of the node + * + * fdt_get_phandle() retrieves the phandle of the device tree node at + * structure block offset nodeoffset. + * + * returns: + * the phandle of the node at nodeoffset, on success (!= 0, != -1) + * 0, if the node has no phandle, or another error occurs + */ +uint32_t fdt_get_phandle(const void *fdt, int nodeoffset); + +/** + * fdt_get_alias_namelen - get alias based on substring + * @fdt: pointer to the device tree blob + * @name: name of the alias th look up + * @namelen: number of characters of name to consider + * + * Identical to fdt_get_alias(), but only examine the first @namelen + * characters of @name for matching the alias name. + * + * Return: a pointer to the expansion of the alias named @name, if it exists, + * NULL otherwise + */ +#ifndef SWIG /* Not available in Python */ +const char *fdt_get_alias_namelen(const void *fdt, + const char *name, int namelen); +#endif + +/** + * fdt_get_alias - retrieve the path referenced by a given alias + * @fdt: pointer to the device tree blob + * @name: name of the alias th look up + * + * fdt_get_alias() retrieves the value of a given alias. That is, the + * value of the property named @name in the node /aliases. + * + * returns: + * a pointer to the expansion of the alias named 'name', if it exists + * NULL, if the given alias or the /aliases node does not exist + */ +const char *fdt_get_alias(const void *fdt, const char *name); + +/** + * fdt_get_path - determine the full path of a node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose path to find + * @buf: character buffer to contain the returned path (will be overwritten) + * @buflen: size of the character buffer at buf + * + * fdt_get_path() computes the full path of the node at offset + * nodeoffset, and records that path in the buffer at buf. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * 0, on success + * buf contains the absolute path of the node at + * nodeoffset, as a NUL-terminated string. + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1) + * characters and will not fit in the given buffer. + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen); + +/** + * fdt_supernode_atdepth_offset - find a specific ancestor of a node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * @supernodedepth: depth of the ancestor to find + * @nodedepth: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_supernode_atdepth_offset() finds an ancestor of the given node + * at a specific depth from the root (where the root itself has depth + * 0, its immediate subnodes depth 1 and so forth). So + * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL); + * will always return 0, the offset of the root node. If the node at + * nodeoffset has depth D, then: + * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL); + * will return nodeoffset itself. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * structure block offset of the node at node offset's ancestor + * of depth supernodedepth (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of + * nodeoffset + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, + int supernodedepth, int *nodedepth); + +/** + * fdt_node_depth - find the depth of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * + * fdt_node_depth() finds the depth of a given node. The root node + * has depth 0, its immediate subnodes depth 1 and so forth. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * depth of the node at nodeoffset (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_depth(const void *fdt, int nodeoffset); + +/** + * fdt_parent_offset - find the parent of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * + * fdt_parent_offset() locates the parent node of a given node (that + * is, it finds the offset of the node which contains the node at + * nodeoffset as a subnode). + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset, *twice*. + * + * returns: + * structure block offset of the parent of the node at nodeoffset + * (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_parent_offset(const void *fdt, int nodeoffset); + +/** + * fdt_node_offset_by_prop_value - find nodes with a given property value + * @fdt: pointer to the device tree blob + * @startoffset: only find nodes after this offset + * @propname: property name to check + * @propval: property value to search for + * @proplen: length of the value in propval + * + * fdt_node_offset_by_prop_value() returns the offset of the first + * node after startoffset, which has a property named propname whose + * value is of length proplen and has value equal to propval; or if + * startoffset is -1, the very first such node in the tree. + * + * To iterate through all nodes matching the criterion, the following + * idiom can be used: + * offset = fdt_node_offset_by_prop_value(fdt, -1, propname, + * propval, proplen); + * while (offset != -FDT_ERR_NOTFOUND) { + * // other code here + * offset = fdt_node_offset_by_prop_value(fdt, offset, propname, + * propval, proplen); + * } + * + * Note the -1 in the first call to the function, if 0 is used here + * instead, the function will never locate the root node, even if it + * matches the criterion. + * + * returns: + * structure block offset of the located node (>= 0, >startoffset), + * on success + * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the + * tree after startoffset + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, + const char *propname, + const void *propval, int proplen); + +/** + * fdt_node_offset_by_phandle - find the node with a given phandle + * @fdt: pointer to the device tree blob + * @phandle: phandle value + * + * fdt_node_offset_by_phandle() returns the offset of the node + * which has the given phandle value. If there is more than one node + * in the tree with the given phandle (an invalid tree), results are + * undefined. + * + * returns: + * structure block offset of the located node (>= 0), on success + * -FDT_ERR_NOTFOUND, no node with that phandle exists + * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1) + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle); + +/** + * fdt_node_check_compatible - check a node's compatible property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @compatible: string to match against + * + * fdt_node_check_compatible() returns 0 if the given node contains a + * @compatible property with the given string as one of its elements, + * it returns non-zero otherwise, or on error. + * + * returns: + * 0, if the node has a 'compatible' property listing the given string + * 1, if the node has a 'compatible' property, but it does not list + * the given string + * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property + * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_check_compatible(const void *fdt, int nodeoffset, + const char *compatible); + +/** + * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value + * @fdt: pointer to the device tree blob + * @startoffset: only find nodes after this offset + * @compatible: 'compatible' string to match against + * + * fdt_node_offset_by_compatible() returns the offset of the first + * node after startoffset, which has a 'compatible' property which + * lists the given compatible string; or if startoffset is -1, the + * very first such node in the tree. + * + * To iterate through all nodes matching the criterion, the following + * idiom can be used: + * offset = fdt_node_offset_by_compatible(fdt, -1, compatible); + * while (offset != -FDT_ERR_NOTFOUND) { + * // other code here + * offset = fdt_node_offset_by_compatible(fdt, offset, compatible); + * } + * + * Note the -1 in the first call to the function, if 0 is used here + * instead, the function will never locate the root node, even if it + * matches the criterion. + * + * returns: + * structure block offset of the located node (>= 0, >startoffset), + * on success + * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the + * tree after startoffset + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_compatible(const void *fdt, int startoffset, + const char *compatible); + +/** + * fdt_stringlist_contains - check a string list property for a string + * @strlist: Property containing a list of strings to check + * @listlen: Length of property + * @str: String to search for + * + * This is a utility function provided for convenience. The list contains + * one or more strings, each terminated by \0, as is found in a device tree + * "compatible" property. + * + * Return: 1 if the string is found in the list, 0 not found, or invalid list + */ +int fdt_stringlist_contains(const char *strlist, int listlen, const char *str); + +/** + * fdt_stringlist_count - count the number of strings in a string list + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @property: name of the property containing the string list + * + * Return: + * the number of strings in the given property + * -FDT_ERR_BADVALUE if the property value is not NUL-terminated + * -FDT_ERR_NOTFOUND if the property does not exist + */ +int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property); + +/** + * fdt_stringlist_search - find a string in a string list and return its index + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @property: name of the property containing the string list + * @string: string to look up in the string list + * + * Note that it is possible for this function to succeed on property values + * that are not NUL-terminated. That's because the function will stop after + * finding the first occurrence of @string. This can for example happen with + * small-valued cell properties, such as #address-cells, when searching for + * the empty string. + * + * return: + * the index of the string in the list of strings + * -FDT_ERR_BADVALUE if the property value is not NUL-terminated + * -FDT_ERR_NOTFOUND if the property does not exist or does not contain + * the given string + */ +int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, + const char *string); + +/** + * fdt_stringlist_get() - obtain the string at a given index in a string list + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @property: name of the property containing the string list + * @index: index of the string to return + * @lenp: return location for the string length or an error code on failure + * + * Note that this will successfully extract strings from properties with + * non-NUL-terminated values. For example on small-valued cell properties + * this function will return the empty string. + * + * If non-NULL, the length of the string (on success) or a negative error-code + * (on failure) will be stored in the integer pointer to by lenp. + * + * Return: + * A pointer to the string at the given index in the string list or NULL on + * failure. On success the length of the string will be stored in the memory + * location pointed to by the lenp parameter, if non-NULL. On failure one of + * the following negative error codes will be returned in the lenp parameter + * (if non-NULL): + * -FDT_ERR_BADVALUE if the property value is not NUL-terminated + * -FDT_ERR_NOTFOUND if the property does not exist + */ +const char *fdt_stringlist_get(const void *fdt, int nodeoffset, + const char *property, int index, + int *lenp); + +/**********************************************************************/ +/* Read-only functions (addressing related) */ +/**********************************************************************/ + +/** + * FDT_MAX_NCELLS - maximum value for #address-cells and #size-cells + * + * This is the maximum value for #address-cells, #size-cells and + * similar properties that will be processed by libfdt. IEE1275 + * requires that OF implementations handle values up to 4. + * Implementations may support larger values, but in practice higher + * values aren't used. + */ +#define FDT_MAX_NCELLS 4 + +/** + * fdt_address_cells - retrieve address size for a bus represented in the tree + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to find the address size for + * + * When the node has a valid #address-cells property, returns its value. + * + * returns: + * 0 <= n < FDT_MAX_NCELLS, on success + * 2, if the node has no #address-cells property + * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid + * #address-cells property + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_address_cells(const void *fdt, int nodeoffset); + +/** + * fdt_size_cells - retrieve address range size for a bus represented in the + * tree + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to find the address range size for + * + * When the node has a valid #size-cells property, returns its value. + * + * returns: + * 0 <= n < FDT_MAX_NCELLS, on success + * 1, if the node has no #size-cells property + * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid + * #size-cells property + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_size_cells(const void *fdt, int nodeoffset); + + +/**********************************************************************/ +/* Write-in-place functions */ +/**********************************************************************/ + +/** + * fdt_setprop_inplace_namelen_partial - change a property's value, + * but not its size + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @namelen: number of characters of name to consider + * @idx: index of the property to change in the array + * @val: pointer to data to replace the property value with + * @len: length of the property value + * + * Identical to fdt_setprop_inplace(), but modifies the given property + * starting from the given index, and using only the first characters + * of the name. It is useful when you want to manipulate only one value of + * an array and you have a string that doesn't end with \0. + * + * Return: 0 on success, negative libfdt error value otherwise + */ +#ifndef SWIG /* Not available in Python */ +int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, + const char *name, int namelen, + uint32_t idx, const void *val, + int len); +#endif + +/** + * fdt_setprop_inplace - change a property's value, but not its size + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: pointer to data to replace the property value with + * @len: length of the property value + * + * fdt_setprop_inplace() replaces the value of a given property with + * the data in val, of length len. This function cannot change the + * size of a property, and so will only work if len is equal to the + * current length of the property. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if len is not equal to the property's current length + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#ifndef SWIG /* Not available in Python */ +int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, + const void *val, int len); +#endif + +/** + * fdt_setprop_inplace_u32 - change the value of a 32-bit integer property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value to replace the property with + * + * fdt_setprop_inplace_u32() replaces the value of a given property + * with the 32-bit integer value in val, converting val to big-endian + * if necessary. This function cannot change the size of a property, + * and so will only work if the property already exists and has length + * 4. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if the property's length is not equal to 4 + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_inplace_u32(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_inplace_u64 - change the value of a 64-bit integer property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 64-bit integer value to replace the property with + * + * fdt_setprop_inplace_u64() replaces the value of a given property + * with the 64-bit integer value in val, converting val to big-endian + * if necessary. This function cannot change the size of a property, + * and so will only work if the property already exists and has length + * 8. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if the property's length is not equal to 8 + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_inplace_u64(void *fdt, int nodeoffset, + const char *name, uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_inplace_cell - change the value of a single-cell property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node containing the property + * @name: name of the property to change the value of + * @val: new value of the 32-bit cell + * + * This is an alternative name for fdt_setprop_inplace_u32() + * Return: 0 on success, negative libfdt error number otherwise. + */ +static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + return fdt_setprop_inplace_u32(fdt, nodeoffset, name, val); +} + +/** + * fdt_nop_property - replace a property with nop tags + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to nop + * @name: name of the property to nop + * + * fdt_nop_property() will replace a given property's representation + * in the blob with FDT_NOP tags, effectively removing it from the + * tree. + * + * This function will alter only the bytes in the blob which contain + * the property, and will not alter or move any other part of the + * tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_nop_property(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_nop_node - replace a node (subtree) with nop tags + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to nop + * + * fdt_nop_node() will replace a given node's representation in the + * blob, including all its subnodes, if any, with FDT_NOP tags, + * effectively removing it from the tree. + * + * This function will alter only the bytes in the blob which contain + * the node and its properties and subnodes, and will not alter or + * move any other part of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_nop_node(void *fdt, int nodeoffset); + +/**********************************************************************/ +/* Sequential write functions */ +/**********************************************************************/ + +/* fdt_create_with_flags flags */ +#define FDT_CREATE_FLAG_NO_NAME_DEDUP 0x1 + /* FDT_CREATE_FLAG_NO_NAME_DEDUP: Do not try to de-duplicate property + * names in the fdt. This can result in faster creation times, but + * a larger fdt. */ + +#define FDT_CREATE_FLAGS_ALL (FDT_CREATE_FLAG_NO_NAME_DEDUP) + +/** + * fdt_create_with_flags - begin creation of a new fdt + * @buf: pointer to memory allocated where fdt will be created + * @bufsize: size of the memory space at fdt + * @flags: a valid combination of FDT_CREATE_FLAG_ flags, or 0. + * + * fdt_create_with_flags() begins the process of creating a new fdt with + * the sequential write interface. + * + * fdt creation process must end with fdt_finished() to produce a valid fdt. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, bufsize is insufficient for a minimal fdt + * -FDT_ERR_BADFLAGS, flags is not valid + */ +int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags); + +/** + * fdt_create - begin creation of a new fdt + * @buf: pointer to memory allocated where fdt will be created + * @bufsize: size of the memory space at fdt + * + * fdt_create() is equivalent to fdt_create_with_flags() with flags=0. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, bufsize is insufficient for a minimal fdt + */ +int fdt_create(void *buf, int bufsize); + +int fdt_resize(void *fdt, void *buf, int bufsize); +int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size); +int fdt_finish_reservemap(void *fdt); +int fdt_begin_node(void *fdt, const char *name); +int fdt_property(void *fdt, const char *name, const void *val, int len); +static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_property(fdt, name, &tmp, sizeof(tmp)); +} +static inline int fdt_property_u64(void *fdt, const char *name, uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_property(fdt, name, &tmp, sizeof(tmp)); +} + +#ifndef SWIG /* Not available in Python */ +static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val) +{ + return fdt_property_u32(fdt, name, val); +} +#endif + +/** + * fdt_property_placeholder - add a new property and return a ptr to its value + * + * @fdt: pointer to the device tree blob + * @name: name of property to add + * @len: length of property value in bytes + * @valp: returns a pointer to where where the value should be placed + * + * returns: + * 0, on success + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_NOSPACE, standard meanings + */ +int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp); + +#define fdt_property_string(fdt, name, str) \ + fdt_property(fdt, name, str, strlen(str)+1) +int fdt_end_node(void *fdt); +int fdt_finish(void *fdt); + +/**********************************************************************/ +/* Read-write functions */ +/**********************************************************************/ + +int fdt_create_empty_tree(void *buf, int bufsize); +int fdt_open_into(const void *fdt, void *buf, int bufsize); +int fdt_pack(void *fdt); + +/** + * fdt_add_mem_rsv - add one memory reserve map entry + * @fdt: pointer to the device tree blob + * @address: 64-bit start address of the reserve map entry + * @size: 64-bit size of the reserved region + * + * Adds a reserve map entry to the given blob reserving a region at + * address address of length size. + * + * This function will insert data into the reserve map and will + * therefore change the indexes of some entries in the table. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new reservation entry + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size); + +/** + * fdt_del_mem_rsv - remove a memory reserve map entry + * @fdt: pointer to the device tree blob + * @n: entry to remove + * + * fdt_del_mem_rsv() removes the n-th memory reserve map entry from + * the blob. + * + * This function will delete data from the reservation table and will + * therefore change the indexes of some entries in the table. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there + * are less than n+1 reserve map entries) + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_del_mem_rsv(void *fdt, int n); + +/** + * fdt_set_name - change the name of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of a node + * @name: name to give the node + * + * fdt_set_name() replaces the name (including unit address, if any) + * of the given node with the given string. NOTE: this function can't + * efficiently check if the new name is unique amongst the given + * node's siblings; results are undefined if this function is invoked + * with a name equal to one of the given node's siblings. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob + * to contain the new name + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_set_name(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_setprop - create or change a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: pointer to data to set the property value to + * @len: length of the property value + * + * fdt_setprop() sets the value of the named property in the given + * node to the given value and length, creating the property if it + * does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len); + +/** + * fdt_setprop_placeholder - allocate space for a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @len: length of the property value + * @prop_data: return pointer to property data + * + * fdt_setprop_placeholer() allocates the named property in the given node. + * If the property exists it is resized. In either case a pointer to the + * property data is returned. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, + int len, void **prop_data); + +/** + * fdt_setprop_u32 - set a property to a 32-bit integer + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value for the property (native endian) + * + * fdt_setprop_u32() sets the value of the named property in the given + * node to the given 32-bit integer value (converting to big-endian if + * necessary), or creates a new property with that value if it does + * not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_u32(void *fdt, int nodeoffset, const char *name, + uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_u64 - set a property to a 64-bit integer + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 64-bit integer value for the property (native endian) + * + * fdt_setprop_u64() sets the value of the named property in the given + * node to the given 64-bit integer value (converting to big-endian if + * necessary), or creates a new property with that value if it does + * not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_u64(void *fdt, int nodeoffset, const char *name, + uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_cell - set a property to a single cell value + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value for the property (native endian) + * + * This is an alternative name for fdt_setprop_u32() + * + * Return: 0 on success, negative libfdt error value otherwise. + */ +static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name, + uint32_t val) +{ + return fdt_setprop_u32(fdt, nodeoffset, name, val); +} + +/** + * fdt_setprop_string - set a property to a string value + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @str: string value for the property + * + * fdt_setprop_string() sets the value of the named property in the + * given node to the given string value (using the length of the + * string to determine the new length of the property), or creates a + * new property with that value if it does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#define fdt_setprop_string(fdt, nodeoffset, name, str) \ + fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) + + +/** + * fdt_setprop_empty - set a property to an empty value + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * + * fdt_setprop_empty() sets the value of the named property in the + * given node to an empty (zero length) value, or creates a new empty + * property if it does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#define fdt_setprop_empty(fdt, nodeoffset, name) \ + fdt_setprop((fdt), (nodeoffset), (name), NULL, 0) + +/** + * fdt_appendprop - append to or create a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to append to + * @val: pointer to data to append to the property value + * @len: length of the data to append to the property value + * + * fdt_appendprop() appends the value to the named property in the + * given node, creating the property if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_appendprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len); + +/** + * fdt_appendprop_u32 - append a 32-bit integer value to a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value to append to the property (native endian) + * + * fdt_appendprop_u32() appends the given 32-bit integer value + * (converting to big-endian if necessary) to the value of the named + * property in the given node, or creates a new property with that + * value if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_appendprop_u32(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_appendprop_u64 - append a 64-bit integer value to a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 64-bit integer value to append to the property (native endian) + * + * fdt_appendprop_u64() appends the given 64-bit integer value + * (converting to big-endian if necessary) to the value of the named + * property in the given node, or creates a new property with that + * value if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_appendprop_u64(void *fdt, int nodeoffset, + const char *name, uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_appendprop_cell - append a single cell value to a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value to append to the property (native endian) + * + * This is an alternative name for fdt_appendprop_u32() + * + * Return: 0 on success, negative libfdt error value otherwise. + */ +static inline int fdt_appendprop_cell(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + return fdt_appendprop_u32(fdt, nodeoffset, name, val); +} + +/** + * fdt_appendprop_string - append a string to a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @str: string value to append to the property + * + * fdt_appendprop_string() appends the given string to the value of + * the named property in the given node, or creates a new property + * with that value if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#define fdt_appendprop_string(fdt, nodeoffset, name, str) \ + fdt_appendprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) + +/** + * fdt_appendprop_addrrange - append a address range property + * @fdt: pointer to the device tree blob + * @parent: offset of the parent node + * @nodeoffset: offset of the node to add a property at + * @name: name of property + * @addr: start address of a given range + * @size: size of a given range + * + * fdt_appendprop_addrrange() appends an address range value (start + * address and size) to the value of the named property in the given + * node, or creates a new property with that value if it does not + * already exist. + * If "name" is not specified, a default "reg" is used. + * Cell sizes are determined by parent's #address-cells and #size-cells. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid + * #address-cells property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADVALUE, addr or size doesn't fit to respective cells size + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain a new property + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset, + const char *name, uint64_t addr, uint64_t size); + +/** + * fdt_delprop - delete a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to nop + * @name: name of the property to nop + * + * fdt_del_property() will delete the given property. + * + * This function will delete data from the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_delprop(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_add_subnode_namelen - creates a new node based on substring + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to create + * @namelen: number of characters of name to consider + * + * Identical to fdt_add_subnode(), but use only the first @namelen + * characters of @name as the name of the new node. This is useful for + * creating subnodes based on a portion of a larger string, such as a + * full path. + * + * Return: structure block offset of the created subnode (>=0), + * negative libfdt error value otherwise + */ +#ifndef SWIG /* Not available in Python */ +int fdt_add_subnode_namelen(void *fdt, int parentoffset, + const char *name, int namelen); +#endif + +/** + * fdt_add_subnode - creates a new node + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * + * fdt_add_subnode() creates a new node as a subnode of the node at + * structure block offset parentoffset, with the given name (which + * should include the unit address, if any). + * + * This function will insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * structure block offset of the created nodeequested subnode (>=0), on + * success + * -FDT_ERR_NOTFOUND, if the requested subnode does not exist + * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE + * tag + * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of + * the given name + * -FDT_ERR_NOSPACE, if there is insufficient free space in the + * blob to contain the new node + * -FDT_ERR_NOSPACE + * -FDT_ERR_BADLAYOUT + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_add_subnode(void *fdt, int parentoffset, const char *name); + +/** + * fdt_del_node - delete a node (subtree) + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to nop + * + * fdt_del_node() will remove the given node, including all its + * subnodes if any, from the blob. + * + * This function will delete data from the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_del_node(void *fdt, int nodeoffset); + +/** + * fdt_overlay_apply - Applies a DT overlay on a base DT + * @fdt: pointer to the base device tree blob + * @fdto: pointer to the device tree overlay blob + * + * fdt_overlay_apply() will apply the given device tree overlay on the + * given base device tree. + * + * Expect the base device tree to be modified, even if the function + * returns an error. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there's not enough space in the base device tree + * -FDT_ERR_NOTFOUND, the overlay points to some inexistant nodes or + * properties in the base DT + * -FDT_ERR_BADPHANDLE, + * -FDT_ERR_BADOVERLAY, + * -FDT_ERR_NOPHANDLES, + * -FDT_ERR_INTERNAL, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADOFFSET, + * -FDT_ERR_BADPATH, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_overlay_apply(void *fdt, void *fdto); + +/** + * fdt_overlay_target_offset - retrieves the offset of a fragment's target + * @fdt: Base device tree blob + * @fdto: Device tree overlay blob + * @fragment_offset: node offset of the fragment in the overlay + * @pathp: pointer which receives the path of the target (or NULL) + * + * fdt_overlay_target_offset() retrieves the target offset in the base + * device tree of a fragment, no matter how the actual targeting is + * done (through a phandle or a path) + * + * returns: + * the targeted node offset in the base device tree + * Negative error code on error + */ +int fdt_overlay_target_offset(const void *fdt, const void *fdto, + int fragment_offset, char const **pathp); + +/**********************************************************************/ +/* Debugging / informational functions */ +/**********************************************************************/ + +const char *fdt_strerror(int errval); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBFDT_H */ diff --git a/components/drivers/ofw/libfdt/libfdt_env.h b/components/drivers/ofw/libfdt/libfdt_env.h new file mode 100644 index 0000000000..07d9b0a97a --- /dev/null +++ b/components/drivers/ofw/libfdt/libfdt_env.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ +#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. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef __CHECKER__ +#define FDT_FORCE __attribute__((force)) +#define FDT_BITWISE __attribute__((bitwise)) +#else +#define FDT_FORCE +#define FDT_BITWISE +#endif + +#include + +#define strnlen rt_strnlen + +typedef uint16_t FDT_BITWISE fdt16_t; +typedef uint32_t FDT_BITWISE fdt32_t; +typedef uint64_t FDT_BITWISE fdt64_t; + +#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 + +#ifdef __APPLE__ +#include + +/* strnlen() is not available on Mac OS < 10.7 */ +# if !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < \ + MAC_OS_X_VERSION_10_7) + +#define strnlen fdt_strnlen + +/* + * fdt_strnlen: returns the length of a string or max_count - which ever is + * smallest. + * Input 1 string: the string whose size is to be determined + * Input 2 max_count: the maximum value returned by this function + * Output: length of the string or max_count (the smallest of the two) + */ +static inline size_t fdt_strnlen(const char *string, size_t max_count) +{ + const char *p = memchr(string, 0, max_count); + return p ? p - string : max_count; +} + +#endif /* !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < + MAC_OS_X_VERSION_10_7) */ + +#endif /* __APPLE__ */ + +#endif /* LIBFDT_ENV_H */ diff --git a/components/drivers/ofw/libfdt/libfdt_internal.h b/components/drivers/ofw/libfdt/libfdt_internal.h new file mode 100644 index 0000000000..16bda1906a --- /dev/null +++ b/components/drivers/ofw/libfdt/libfdt_internal.h @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ +#ifndef LIBFDT_INTERNAL_H +#define LIBFDT_INTERNAL_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include + +#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) +#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE)) + +int32_t fdt_ro_probe_(const void *fdt); +#define FDT_RO_PROBE(fdt) \ + { \ + int32_t totalsize_; \ + if ((totalsize_ = fdt_ro_probe_(fdt)) < 0) \ + return totalsize_; \ + } + +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); +} + +/* + * Internal helpers to access tructural elements of the device tree + * blob (rather than for exaple reading integers from within property + * values). We assume that we are either given a naturally aligned + * address for the platform or if we are not, we are on a platform + * where unaligned memory reads will be handled in a graceful manner. + * If not the external helpers fdtXX_ld() from libfdt.h can be used + * instead. + */ +static inline uint32_t fdt32_ld_(const fdt32_t *p) +{ + return fdt32_to_cpu(*p); +} + +static inline uint64_t fdt64_ld_(const fdt64_t *p) +{ + return fdt64_to_cpu(*p); +} + +#define FDT_SW_MAGIC (~FDT_MAGIC) + +/**********************************************************************/ +/* Checking controls */ +/**********************************************************************/ + +#ifndef FDT_ASSUME_MASK +#define FDT_ASSUME_MASK 0 +#endif + +/* + * Defines assumptions which can be enabled. Each of these can be enabled + * individually. For maximum safety, don't enable any assumptions! + * + * For minimal code size and no safety, use ASSUME_PERFECT at your own risk. + * You should have another method of validating the device tree, such as a + * signature or hash check before using libfdt. + * + * For situations where security is not a concern it may be safe to enable + * ASSUME_SANE. + */ +enum { + /* + * This does essentially no checks. Only the latest device-tree + * version is correctly handled. Inconsistencies or errors in the device + * tree may cause undefined behaviour or crashes. Invalid parameters + * passed to libfdt may do the same. + * + * If an error occurs when modifying the tree it may leave the tree in + * an intermediate (but valid) state. As an example, adding a property + * where there is insufficient space may result in the property name + * being added to the string table even though the property itself is + * not added to the struct section. + * + * Only use this if you have a fully validated device tree with + * the latest supported version and wish to minimise code size. + */ + ASSUME_PERFECT = 0xff, + + /* + * This assumes that the device tree is sane. i.e. header metadata + * and basic hierarchy are correct. + * + * With this assumption enabled, normal device trees produced by libfdt + * and the compiler should be handled safely. Malicious device trees and + * complete garbage may cause libfdt to behave badly or crash. Truncated + * device trees (e.g. those only partially loaded) can also cause + * problems. + * + * Note: Only checks that relate exclusively to the device tree itself + * (not the parameters passed to libfdt) are disabled by this + * assumption. This includes checking headers, tags and the like. + */ + ASSUME_VALID_DTB = 1 << 0, + + /* + * This builds on ASSUME_VALID_DTB and further assumes that libfdt + * functions are called with valid parameters, i.e. not trigger + * FDT_ERR_BADOFFSET or offsets that are out of bounds. It disables any + * extensive checking of parameters and the device tree, making various + * assumptions about correctness. + * + * It doesn't make sense to enable this assumption unless + * ASSUME_VALID_DTB is also enabled. + */ + ASSUME_VALID_INPUT = 1 << 1, + + /* + * This disables checks for device-tree version and removes all code + * which handles older versions. + * + * Only enable this if you know you have a device tree with the latest + * version. + */ + ASSUME_LATEST = 1 << 2, + + /* + * This assumes that it is OK for a failed addition to the device tree, + * due to lack of space or some other problem, to skip any rollback + * steps (such as dropping the property name from the string table). + * This is safe to enable in most circumstances, even though it may + * leave the tree in a sub-optimal state. + */ + ASSUME_NO_ROLLBACK = 1 << 3, + + /* + * This assumes that the device tree components appear in a 'convenient' + * order, i.e. the memory reservation block first, then the structure + * block and finally the string block. + * + * This order is not specified by the device-tree specification, + * but is expected by libfdt. The device-tree compiler always created + * device trees with this order. + * + * This assumption disables a check in fdt_open_into() and removes the + * ability to fix the problem there. This is safe if you know that the + * device tree is correctly ordered. See fdt_blocks_misordered_(). + */ + ASSUME_LIBFDT_ORDER = 1 << 4, + + /* + * This assumes that libfdt itself does not have any internal bugs. It + * drops certain checks that should never be needed unless libfdt has an + * undiscovered bug. + * + * This can generally be considered safe to enable. + */ + ASSUME_LIBFDT_FLAWLESS = 1 << 5, +}; + +/** + * can_assume_() - check if a particular assumption is enabled + * + * @mask: Mask to check (ASSUME_...) + * @return true if that assumption is enabled, else false + */ +static inline bool can_assume_(int mask) +{ + return FDT_ASSUME_MASK & mask; +} + +/** helper macros for checking assumptions */ +#define can_assume(_assume) can_assume_(ASSUME_ ## _assume) + +#endif /* LIBFDT_INTERNAL_H */ diff --git a/components/drivers/ofw/ofw.c b/components/drivers/ofw/ofw.c new file mode 100755 index 0000000000..88a5fe963d --- /dev/null +++ b/components/drivers/ofw/ofw.c @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-25 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "rtdm.ofw" +#define DBG_LVL DBG_INFO +#include + +#include "ofw_internal.h" + +struct rt_ofw_stub *rt_ofw_stub_probe_range(struct rt_ofw_node *np, + const struct rt_ofw_stub *stub_start, const struct rt_ofw_stub *stub_end) +{ + const struct rt_ofw_stub *stub = RT_NULL; + + if (np && stub_start && stub_end && + !rt_ofw_node_test_flag(np, RT_OFW_F_READLY) && + !rt_ofw_node_test_flag(np, RT_OFW_F_SYSTEM)) + { + struct rt_ofw_prop *compat_prop = rt_ofw_get_prop(np, "compatible", RT_NULL); + + if (compat_prop) + { + rt_ofw_foreach_stub(stub, stub_start, stub_end) + { + struct rt_ofw_node_id *id; + + if (!stub->ids) + { + continue; + } + + id = rt_ofw_prop_match(compat_prop, stub->ids); + + if (id) + { + if (!stub->handler(np, id)) + { + rt_ofw_node_set_flag(np, RT_OFW_F_READLY); + } + + break; + } + } + } + } + + return (struct rt_ofw_stub *)stub; +} + +static const char *ofw_console_serial_find(char *dst_con, struct rt_ofw_node *np) +{ + rt_object_t rt_obj; + const char *ofw_name = RT_NULL; + struct rt_serial_device *rt_serial = rt_ofw_data(np); + + if (rt_serial) + { + rt_obj = &rt_serial->parent.parent; + } + + /* + * This is a dangerous behavior because rt_data can be modified by other + * user. But fortunately, we can check if the rt_data is a rt_device or + * rt_serial_device. + */ + if (rt_obj && rt_object_get_type(rt_obj) == RT_Object_Class_Device && + rt_serial->parent.type == RT_Device_Class_Char) + { + ofw_name = np->full_name; + rt_strncpy(dst_con, rt_obj->name, RT_NAME_MAX); + } + + return ofw_name; +} + +static const char *ofw_console_tty_find(char *dst_con, const char *con) +{ + const char *ofw_name = RT_NULL; + static rt_bus_t platform_bus = RT_NULL; + + if (!platform_bus) + { + platform_bus = rt_bus_find_by_name("platform"); + } + + if (platform_bus) + { + rt_device_t dev; + rt_ubase_t level; + const char *console = con; + int tty_idx = 0, tty_id = 0, tty_name_len; + + con += sizeof("tty") - 1; + + while (*con && !(*con >= '0' && *con <= '9')) + { + ++con; + } + + tty_name_len = con - console; + + while (*con && *con >= '0' && *con <= '9') + { + tty_id *= 10; + tty_id += *con - '0'; + + ++con; + } + + level = rt_spin_lock_irqsave(&platform_bus->spinlock); + + rt_list_for_each_entry(dev, &platform_bus->dev_list, node) + { + struct rt_platform_device *pdev = rt_container_of(dev, struct rt_platform_device, parent); + const struct rt_ofw_node_id *id = pdev->id; + + if (id && id->type[0] && !rt_strncmp(id->type, console, tty_name_len)) + { + if (tty_idx == tty_id) + { + ofw_name = ofw_console_serial_find(dst_con, pdev->parent.ofw_node); + + break; + } + + ++tty_idx; + } + } + + rt_spin_unlock_irqrestore(&platform_bus->spinlock, level); + } + + return ofw_name; +} + +rt_err_t rt_ofw_console_setup(void) +{ + rt_err_t err = -RT_ENOSYS; + char con_name[RT_NAME_MAX]; + const char *ofw_name = RT_NULL, *stdout_path, *con; + + /* chosen.console > chosen.stdout-path > RT_CONSOLE_DEVICE_NAME */ + + con = rt_ofw_bootargs_select("console=", 0); + + for (int i = 1; con; ++i) + { + if (!rt_strncmp(con, "uart", sizeof("uart") - 1)) + { + rt_strncpy(con_name, con, RT_NAME_MAX); + + err = RT_EOK; + break; + } + else if (!rt_strncmp(con, "tty", sizeof("tty") - 1)) + { + ofw_name = ofw_console_tty_find(con_name, con); + + if (ofw_name) + { + err = RT_EOK; + break; + } + } + + con = rt_ofw_bootargs_select("console=", i); + } + + if (err == -RT_ENOSYS && !rt_ofw_prop_read_string(ofw_node_chosen, "stdout-path", &stdout_path)) + { + struct rt_ofw_node *stdout_np = rt_ofw_find_node_by_path(stdout_path); + + if (stdout_np && (ofw_name = ofw_console_serial_find(con_name, stdout_np))) + { + err = RT_EOK; + } + } + + if (err == -RT_ENOSYS) + { + rt_device_t serial = rt_device_find(RT_CONSOLE_DEVICE_NAME); + + if (serial) + { + ofw_name = rt_ofw_node_full_name(serial->ofw_node); + + con = RT_CONSOLE_DEVICE_NAME; + } + else + { + LOG_W("Console will probably fail to setup"); + } + } + else + { + con = con_name; + } + + rt_console_set_device(con); + + rt_fdt_earlycon_kick(FDT_EARLYCON_KICK_COMPLETED); + + LOG_I("Console: %s (%s)", con, ofw_name ? ofw_name : ""); + + return err; +} + +const char *rt_ofw_bootargs_select(const char *key, int index) +{ + const char *value = RT_NULL; + + if (key && index >= 0) + { + static char **values = RT_NULL; + static rt_size_t bootargs_nr = 1; + const char *bootargs = RT_NULL, *ch; + + if (bootargs_nr && !values && + (bootargs_nr = 0, !rt_ofw_prop_read_string(ofw_node_chosen, "bootargs", &bootargs)) && + bootargs && (bootargs = rt_strdup(bootargs))) + { + rt_bool_t quotes = RT_TRUE; + rt_size_t length = rt_strlen(bootargs); + const char *bootargs_end = bootargs + length; + + for (ch = bootargs; ch < bootargs_end; ++ch) + { + if (*ch == '"') + { + quotes = !quotes; + continue; + } + + if (*ch != ' ' || !quotes) + { + continue; + } + + ++bootargs_nr; + + while (*ch == ' ' && ch < bootargs_end) + { + *(char *)ch++ = '\0'; + } + --ch; + } + + if (bootargs_nr) + { + /* last arg */ + ++bootargs_nr; + values = rt_malloc(sizeof(char *) * bootargs_nr); + } + + if (values) + { + int idx = 0; + + for (int i = 0; i < length; ++i) + { + for (; i < length && !bootargs[i]; ++i) + { + } + + if (i < length) + { + values[idx++] = (char *)&bootargs[i]; + } + + for (; i < length && bootargs[i]; ++i) + { + } + } + } + else + { + rt_free((char *)bootargs); + } + } + + if (values) + { + int keylen = rt_strlen(key); + + for (int idx = 0, count = 0; idx < bootargs_nr; ++idx) + { + if (!rt_strncmp(values[idx], key, keylen)) + { + if (count == index) + { + value = values[idx] + keylen; + + break; + } + + ++count; + } + } + } + } + + return value; +} + +#ifdef RT_USING_CONSOLE +static void dts_put_depth(int depth) +{ + while (depth --> 0) + { + rt_kputs(" "); + } +} + +static rt_bool_t dts_test_string_list(const void *value, int size) +{ + const char *str, *str_start, *str_end; + + if (!size) + { + return RT_FALSE; + } + + str = value; + + /* 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 ofw_node_dump_dts(struct rt_ofw_node *np, rt_bool_t sibling_too) +{ + int depth = 0; + struct rt_ofw_prop *prop; + struct rt_ofw_node *org_np = np; + + while (np) + { + dts_put_depth(depth); + rt_kputs(np->full_name); + rt_kputs(" {\n"); + + prop = np->props; + + /* Print prop start */ + ++depth; + + while (prop) + { + dts_put_depth(depth); + + rt_kputs(prop->name); + + /* Have value? */ + if (prop->length > 0) + { + int length = prop->length; + void *value = prop->value; + + rt_kputs(" = "); + + if (dts_test_string_list(value, length)) + { + /* String list */ + char *str = value; + + do { + rt_kputs("\""); + rt_kputs(str); + rt_kputs("\", "); + + str += rt_strlen(str) + 1; + + } while (str < (char *)value + length); + + rt_kputs("\b\b"); + } + else if ((length % 4) == 0) + { + /* u32 data in */ + fdt32_t *cell = value; + + rt_kputs("<"); + + length /= 4; + + for (int i = 0; i < length; ++i) + { + rt_kprintf("0x%02x ", fdt32_to_cpu(cell[i])); + } + + rt_kputs("\b>"); + } + else + { + /* Byte data in [?] */ + rt_uint8_t *byte = value; + + rt_kputs("["); + + for (int i = 0; i < length; ++i) + { + rt_kprintf("%02x ", *byte++); + } + + rt_kputs("\b]"); + } + } + + rt_kputs(";\n"); + + prop = prop->next; + } + + --depth; + /* Print prop end */ + + if (np->child) + { + rt_kputs("\n"); + np = np->child; + ++depth; + } + else + { + dts_put_depth(depth); + rt_kputs("};\n"); + + while (np->parent && !np->sibling) + { + np = np->parent; + --depth; + + if (depth >= 0) + { + dts_put_depth(depth); + rt_kputs("};\n"); + } + } + + if (!sibling_too && org_np == np) + { + break; + } + + np = np->sibling; + + if (np) + { + rt_kputs("\n"); + } + } + } +} + +void rt_ofw_node_dump_dts(struct rt_ofw_node *np, rt_bool_t sibling_too) +{ + if (np) + { + if (!rt_strcmp(np->name, "/")) + { + struct fdt_info *header = (struct fdt_info *)np->name; + struct fdt_reserve_entry *rsvmap = header->rsvmap; + + rt_kprintf("/dts-v%x/;\n\n", fdt_version(header->fdt)); + + for (int i = header->rsvmap_nr - 1; i >= 0; --i) + { + rt_kprintf("/memreserve/\t%p %p;\n", + ofw_static_cast(rt_size_t, fdt64_to_cpu(rsvmap->address)), + ofw_static_cast(rt_size_t, fdt64_to_cpu(rsvmap->size))); + + ++rsvmap; + } + + rt_kputs(np->name); + } + + ofw_node_dump_dts(np, sibling_too); + } +} + +#ifdef RT_USING_MSH +static void ofw_dts(int argc, char**argv) +{ + if (ofw_node_root) + { + if (argc == 1) + { + rt_ofw_node_dump_dts(ofw_node_root, RT_TRUE); + } + else if (argv[1][0] == '/') + { + struct rt_ofw_node *np = rt_ofw_find_node_by_path(argv[1]); + + if (np) + { + rt_ofw_node_dump_dts(np, RT_FALSE); + } + else + { + rt_kprintf("%s not found.\n", argv[1]); + } + } + else if (argc >= 2 && !rt_strcmp(argv[1], "list") && argv[2][0] == '/') + { + struct rt_ofw_node *np = rt_ofw_find_node_by_path(argv[2]); + + if (np) + { + const char *split = np == ofw_node_root ? "" : "/"; + + np = np->child; + + while (np) + { + rt_kprintf("%s%s%s\n", argv[2], split, np->full_name); + np = np->sibling; + } + } + else + { + rt_kprintf("%s not found.\n", argv[2]); + } + } + else + { + rt_kprintf("Usage: %s {list} {path}\n", __func__); + } + } + else + { + rt_kprintf("\"/\" path not found."); + } +} +MSH_CMD_EXPORT(ofw_dts, dump the dts or node for this platform); +#endif /* RT_USING_MSH */ +#endif /* RT_USING_CONSOLE */ diff --git a/components/drivers/ofw/ofw_internal.h b/components/drivers/ofw/ofw_internal.h new file mode 100755 index 0000000000..619cf4a922 --- /dev/null +++ b/components/drivers/ofw/ofw_internal.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-25 GuEe-GUI first version + */ + +#ifndef __OFW_INTERNAL_H__ +#define __OFW_INTERNAL_H__ + +#include + +#include + +#define OFW_PHANDLE_MIN 1 +#define OFW_PHANDLE_MAX FDT_MAX_PHANDLE + +#define OFW_NODE_MAX_DEPTH 64 +#define OFW_NODE_MIN_HASH 128 + +#define OFW_ROOT_NODE_ADDR_CELLS_DEFAULT 1 +#define OFW_ROOT_NODE_SIZE_CELLS_DEFAULT 1 + +struct fdt_info +{ + /* Always "/", because we save "ofw" information in root node. */ + char name[sizeof("/")]; + + /* Information start */ + void *fdt; + + /* Only root can use */ + struct fdt_reserve_entry *rsvmap; + rt_size_t rsvmap_nr; +}; + +struct alias_info +{ + rt_list_t list; + + int id; + const char *tag; + rt_size_t tag_len; + + struct rt_ofw_node *np; +}; + +struct bus_ranges +{ + rt_size_t nr; + + rt_uint64_t *child_addr; + rt_uint64_t *parent_addr; + rt_uint64_t *child_size; +}; + +extern struct rt_ofw_node *ofw_node_root; +extern struct rt_ofw_node *ofw_node_cpus; +extern struct rt_ofw_node *ofw_node_chosen; +extern struct rt_ofw_node *ofw_node_aliases; +extern struct rt_ofw_node *ofw_node_reserved_memory; + +extern struct rt_fdt_earlycon fdt_earlycon; + +#define ofw_static_cast(to_type, value) \ + (to_type)(((value) >> ((sizeof(value) - sizeof(to_type)) * 8))) + +rt_err_t ofw_alias_scan(void); +rt_err_t ofw_phandle_hash_reset(rt_phandle min, rt_phandle max); + +#endif /* __OFW_INTERNAL_H__ */ diff --git a/components/drivers/ofw/raw.c b/components/drivers/ofw/raw.c new file mode 100755 index 0000000000..35e7aaa343 --- /dev/null +++ b/components/drivers/ofw/raw.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-19 GuEe-GUI first version + */ + +#include + +int fdt_add_subnode_possible(void *fdt, int parentoffset, const char *name) +{ + int nodeoffset; + + if ((nodeoffset = fdt_add_subnode(fdt, parentoffset, name)) < 0) + { + fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + FDT_PADDING_SIZE); + nodeoffset = fdt_add_subnode(fdt, parentoffset, name); + } + + return nodeoffset; +} + +int fdt_add_mem_rsv_possible(void *fdt, size_t addr, size_t size) +{ + int err = 0; + + if (fdt_add_mem_rsv(fdt, addr, size) < 0) + { + fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + FDT_PADDING_SIZE); + err = fdt_add_mem_rsv(fdt, addr, size); + } + + return err; +} + +int fdt_setprop_uxx(void *fdt, int nodeoffset, const char *name, uint64_t val, bool is_u64) +{ + int err; + + if (is_u64) + { + err = fdt_setprop_u64(fdt, nodeoffset, name, val); + } + else + { + err = fdt_setprop_u32(fdt, nodeoffset, name, (uint32_t)val); + } + + return err; +} + +#define FDT_RAW_GET_VAL_FLAG(std_type, s, sz) \ +int fdt_getprop_##std_type##sz(void *fdt, int nodeoffset, \ + const char *name, s##int##sz##_t *out_value, int *lenp) \ +{ \ + int err = -FDT_ERR_NOTFOUND; \ + if (fdt && nodeoffset >= 0 && name && out_value) \ + { \ + const fdt##sz##_t *ptr; \ + if ((ptr = fdt_getprop(fdt, nodeoffset, name, lenp))) \ + { \ + *out_value = fdt##sz##_to_cpu(*ptr); \ + err = 0; \ + } \ + } \ + return err; \ +} + +#define FDT_RAW_GET_VAL(size) \ + FDT_RAW_GET_VAL_FLAG(u, u, size) \ + FDT_RAW_GET_VAL_FLAG(s, , size) + +FDT_RAW_GET_VAL(64) +FDT_RAW_GET_VAL(32) +FDT_RAW_GET_VAL(16) +FDT_RAW_GET_VAL(8) + +#undef FDT_RAW_GET_VAL +#undef FDT_RAW_GET_VAL_FLAG + +int fdt_io_addr_cells(void *fdt, int nodeoffset) +{ + int cells = -1; + int parentoffset = fdt_parent_offset(fdt, nodeoffset); + + for (; parentoffset >= 0 ; parentoffset = fdt_parent_offset(fdt, parentoffset)) + { + const fdt32_t *cells_tmp = fdt_getprop(fdt, parentoffset, "#address-cells", NULL); + + if (cells_tmp) + { + cells = fdt32_to_cpu(*cells_tmp); + } + } + + if (cells < 0) + { + cells = fdt_address_cells(fdt, nodeoffset); + } + + return cells; +} + +int fdt_io_size_cells(void *fdt, int nodeoffset) +{ + int cells = -1; + int parentoffset = fdt_parent_offset(fdt, nodeoffset); + + for (; parentoffset >= 0 ; parentoffset = fdt_parent_offset(fdt, parentoffset)) + { + const fdt32_t *cells_tmp = fdt_getprop(fdt, parentoffset, "#size-cells", NULL); + + if (cells_tmp) + { + cells = fdt32_to_cpu(*cells_tmp); + } + } + + if (cells < 0) + { + cells = fdt_size_cells(fdt, nodeoffset); + } + + return cells; +} + +int fdt_install_initrd(void *fdt, char *os_name, size_t initrd_addr, size_t initrd_size) +{ + int err = -FDT_ERR_NOTFOUND; + int chosen_offset = -1, root_off = fdt_path_offset(fdt, "/"); + + if (root_off >= 0) + { + chosen_offset = fdt_subnode_offset(fdt, root_off, "chosen"); + + if (chosen_offset == -FDT_ERR_NOTFOUND) + { + chosen_offset = fdt_add_subnode_possible(fdt, root_off, "chosen"); + } + } + + if (chosen_offset >= 0) + { + uint64_t addr, size; + + err = 0; + + /* Update the entry */ + for (int i = fdt_num_mem_rsv(fdt) - 1; i >= 0; --i) + { + fdt_get_mem_rsv(fdt, i, &addr, &size); + + 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_PADDING_SIZE); + + if (fdt_add_mem_rsv(fdt, initrd_addr, initrd_size) < 0) + { + err = -FDT_ERR_NOSPACE; + } + } + + if (!err) + { + size_t name_len; + char initrd_name[64]; + bool is_u64 = (fdt_io_addr_cells(fdt, root_off) == 2); + + if (!os_name) + { + os_name = "rt-thread"; + } + + name_len = strlen(initrd_name); + + strncpy(&initrd_name[name_len], ",initrd-start", sizeof(initrd_name) - name_len); + fdt_setprop_uxx(fdt, chosen_offset, initrd_name, initrd_addr, is_u64); + + strncpy(&initrd_name[name_len], ",initrd-end", sizeof(initrd_name) - name_len); + fdt_setprop_uxx(fdt, chosen_offset, initrd_name, initrd_addr + initrd_size, is_u64); + } + } + + return err; +} diff --git a/components/drivers/pic/Kconfig b/components/drivers/pic/Kconfig new file mode 100755 index 0000000000..cadb52b669 --- /dev/null +++ b/components/drivers/pic/Kconfig @@ -0,0 +1,11 @@ +menuconfig RT_USING_PIC + bool "Using Programmable Interrupt Controller (PIC)" + select RT_USING_BITMAP + depends on RT_USING_DM + default n + +config MAX_HANDLERS + int "IRQ max handlers" + depends on RT_USING_PIC + range 1 4294967294 + default 256 diff --git a/components/drivers/pic/SConscript b/components/drivers/pic/SConscript new file mode 100644 index 0000000000..ade23fc309 --- /dev/null +++ b/components/drivers/pic/SConscript @@ -0,0 +1,15 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_PIC']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['pic.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/pic/pic.c b/components/drivers/pic/pic.c new file mode 100644 index 0000000000..e5bc0fed7c --- /dev/null +++ b/components/drivers/pic/pic.c @@ -0,0 +1,977 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-24 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "rtdm.pic" +#define DBG_LVL DBG_INFO +#include + +#include + +struct irq_traps +{ + rt_list_t list; + + void *data; + rt_bool_t (*handler)(void *); +}; + +static int _ipi_hash[] = +{ +#ifdef RT_USING_SMP + [RT_SCHEDULE_IPI] = RT_SCHEDULE_IPI, + [RT_STOP_IPI] = RT_STOP_IPI, +#endif +}; + +/* reserved ipi */ +static int _pirq_hash_idx = RT_ARRAY_SIZE(_ipi_hash); +static struct rt_pic_irq _pirq_hash[MAX_HANDLERS] = +{ + [0 ... MAX_HANDLERS - 1] = + { + .irq = -1, + .hwirq = -1, + .mode = RT_IRQ_MODE_NONE, + .priority = RT_UINT32_MAX, + .rw_lock = { }, + } +}; + +static struct rt_spinlock _pic_lock = { }; +static rt_size_t _pic_name_max = sizeof("PIC"); +static rt_list_t _pic_nodes = RT_LIST_OBJECT_INIT(_pic_nodes); +static rt_list_t _traps_nodes = RT_LIST_OBJECT_INIT(_traps_nodes); + +static struct rt_pic_irq *irq2pirq(int irq) +{ + struct rt_pic_irq *pirq; + + if (irq >= 0 && irq < MAX_HANDLERS) + { + pirq = &_pirq_hash[irq]; + + if (pirq->irq < 0) + { + pirq = RT_NULL; + } + } + + if (!pirq) + { + LOG_E("irq = %d is invalid", irq); + } + + return pirq; +} + +static void append_pic(struct rt_pic *pic) +{ + int pic_name_len = rt_strlen(pic->ops->name); + + rt_list_insert_before(&_pic_nodes, &pic->list); + + if (pic_name_len > _pic_name_max) + { + _pic_name_max = pic_name_len; + } +} + +rt_err_t rt_pic_linear_irq(struct rt_pic *pic, rt_size_t irq_nr) +{ + rt_err_t err = RT_EOK; + + if (pic && pic->ops && pic->ops->name) + { + rt_ubase_t level = rt_spin_lock_irqsave(&_pic_lock); + + if (_pirq_hash_idx + irq_nr <= RT_ARRAY_SIZE(_pirq_hash)) + { + rt_list_init(&pic->list); + + pic->irq_start = _pirq_hash_idx; + pic->irq_nr = irq_nr; + pic->pirqs = &_pirq_hash[_pirq_hash_idx]; + + _pirq_hash_idx += irq_nr; + + append_pic(pic); + + LOG_D("%s alloc irqs ranges [%d, %d]", pic->ops->name, + pic->irq_start, pic->irq_start + pic->irq_nr); + } + else + { + LOG_E("%s alloc %d irqs is overflow", pic->ops->name, irq_nr); + + err = -RT_EEMPTY; + } + + rt_spin_unlock_irqrestore(&_pic_lock, level); + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +static void config_pirq(struct rt_pic *pic, struct rt_pic_irq *pirq, int irq, int hwirq) +{ + rt_ubase_t level = rt_spin_lock_irqsave(&pirq->rw_lock); + + pirq->irq = irq; + pirq->hwirq = hwirq; + + pirq->pic = pic; + + rt_list_init(&pirq->isr.list); + + rt_spin_unlock_irqrestore(&pirq->rw_lock, level); +} + +int rt_pic_config_ipi(struct rt_pic *pic, int ipi_index, int hwirq) +{ + int ipi = ipi_index; + + if (pic && ipi < RT_ARRAY_SIZE(_ipi_hash) && hwirq >= 0 && pic->ops->irq_send_ipi) + { + config_pirq(pic, &_pirq_hash[ipi], ipi, hwirq); + + LOG_D("%s config %s %d to hwirq %d", pic->ops->name, "ipi", ipi, hwirq); + } + else + { + ipi = -RT_EINVAL; + } + + return ipi; +} + +int rt_pic_config_irq(struct rt_pic *pic, int irq_index, int hwirq) +{ + int irq; + + if (pic && hwirq >= 0) + { + irq = pic->irq_start + irq_index; + + if (irq >= 0 && irq < MAX_HANDLERS) + { + config_pirq(pic, &_pirq_hash[irq], irq, hwirq); + + LOG_D("%s config %s %d to hwirq %d", pic->ops->name, "irq", irq, hwirq); + } + else + { + irq = -RT_ERROR; + } + } + else + { + irq = -RT_EINVAL; + } + + return irq; +} + +struct rt_pic_irq *rt_pic_find_ipi(struct rt_pic *pic, int ipi_index) +{ + struct rt_pic_irq *pirq = &_pirq_hash[ipi_index]; + + RT_ASSERT(ipi_index < RT_ARRAY_SIZE(_ipi_hash)); + RT_ASSERT(pirq->pic == pic); + + return pirq; +} + +int rt_pic_cascade(struct rt_pic *pic, struct rt_pic *parent_pic, int hwirq, rt_uint32_t mode) +{ + int irq = -RT_EINVAL; + + if (pic && parent_pic && hwirq >= 0) + { + struct rt_pic *ppic = parent_pic; + rt_ubase_t level = rt_spin_lock_irqsave(&_pic_lock); + + while (!ppic->ops->irq_map && ppic->parent) + { + ppic = ppic->parent; + } + + rt_spin_unlock_irqrestore(&_pic_lock, level); + + if (ppic->ops->irq_map) + { + struct rt_pic_irq *pirq; + + irq = ppic->ops->irq_map(ppic, hwirq, mode); + + if (irq >= 0 && (pirq = irq2pirq(irq))) + { + rt_spin_lock(&pirq->rw_lock); + + pirq->pic = pic; + pic->parent = parent_pic; + + rt_spin_unlock(&pirq->rw_lock); + + if (rt_list_isempty(&pic->list)) + { + rt_ubase_t level = rt_spin_lock_irqsave(&_pic_lock); + + append_pic(pic); + + rt_spin_unlock_irqrestore(&_pic_lock, level); + } + } + } + else + { + irq = -RT_ENOSYS; + } + } + + return irq; +} + +void rt_pic_uncascade(struct rt_pic *pic, int irq) +{ + struct rt_pic_irq *pirq; + + if (pic && pic->parent && irq >= 0 && (pirq = irq2pirq(irq))) + { + struct rt_pic *ppic, *prev = RT_NULL; + + rt_spin_lock(&pirq->rw_lock); + + ppic = pirq->pic; + + while (ppic && pic->parent != ppic->parent) + { + prev = ppic; + ppic = ppic->parent; + } + + if (ppic) + { + if (prev) + { + pirq->pic = prev; + prev->parent = pic->parent; + } + else + { + pirq->pic = pic->parent; + } + + pic->parent = RT_NULL; + } + + rt_spin_unlock(&pirq->rw_lock); + } +} + +rt_err_t rt_pic_attach_irq(int irq, rt_isr_handler_t handler, void *uid, const char *name, int flags) +{ + rt_err_t err = -RT_EINVAL; + struct rt_pic_irq *pirq; + + if (handler && name && (pirq = irq2pirq(irq))) + { + struct rt_pic_isr *isr = RT_NULL; + rt_ubase_t level = rt_spin_lock_irqsave(&pirq->rw_lock); + + err = RT_EOK; + + if (!pirq->isr.action.handler) + { + /* first attach */ + isr = &pirq->isr; + rt_list_init(&isr->list); + } + else + { + rt_spin_unlock_irqrestore(&pirq->rw_lock, level); + + if ((isr = rt_malloc(sizeof(*isr)))) + { + rt_list_init(&isr->list); + + level = rt_spin_lock_irqsave(&pirq->rw_lock); + + rt_list_insert_after(&pirq->isr.list, &isr->list); + } + else + { + LOG_E("No memory to save '%s' isr", name); + err = -RT_ERROR; + } + } + + if (!err) + { + isr->flags = flags; + isr->action.handler = handler; + isr->action.param = uid; + #ifdef RT_USING_INTERRUPT_INFO + isr->action.counter = 0; + rt_strncpy(isr->action.name, name, RT_NAME_MAX); + #endif + + rt_spin_unlock_irqrestore(&pirq->rw_lock, level); + } + } + + return err; +} + +rt_err_t rt_pic_detach_irq(int irq, void *uid) +{ + rt_err_t err = -RT_EINVAL; + struct rt_pic_irq *pirq = irq2pirq(irq); + + if (pirq) + { + rt_bool_t will_free = RT_FALSE; + struct rt_pic_isr *isr = RT_NULL; + rt_ubase_t level = rt_spin_lock_irqsave(&pirq->rw_lock); + + isr = &pirq->isr; + + if (isr->action.param == uid) + { + if (rt_list_isempty(&isr->list)) + { + isr->action.handler = RT_NULL; + isr->action.param = RT_NULL; + } + else + { + struct rt_pic_isr *next_isr = rt_list_entry(isr->list.next, struct rt_pic_isr, list); + + rt_list_remove(&next_isr->list); + + isr->action.handler = next_isr->action.handler; + isr->action.param = next_isr->action.param; + #ifdef RT_USING_INTERRUPT_INFO + isr->action.counter = next_isr->action.counter; + rt_strncpy(isr->action.name, next_isr->action.name, RT_NAME_MAX); + #endif + + isr = next_isr; + will_free = RT_TRUE; + } + + err = RT_EOK; + } + else + { + rt_list_for_each_entry(isr, &pirq->isr.list, list) + { + if (isr->action.param == uid) + { + err = RT_EOK; + + will_free = RT_TRUE; + rt_list_remove(&isr->list); + break; + } + } + } + + rt_spin_unlock_irqrestore(&pirq->rw_lock, level); + + if (will_free) + { + rt_free(isr); + } + } + + return err; +} + +rt_err_t rt_pic_add_traps(rt_bool_t (*handler)(void *), void *data) +{ + rt_err_t err = -RT_EINVAL; + + if (handler) + { + struct irq_traps *traps = rt_malloc(sizeof(*traps)); + + if (traps) + { + rt_ubase_t level = rt_hw_interrupt_disable(); + + rt_list_init(&traps->list); + + traps->data = data; + traps->handler = handler; + + rt_list_insert_before(&_traps_nodes, &traps->list); + err = RT_EOK; + + rt_hw_interrupt_enable(level); + } + else + { + LOG_E("No memory to save '%p' handler", handler); + err = -RT_ENOMEM; + } + } + + return err; +} + +rt_err_t rt_pic_do_traps(void) +{ + rt_err_t err = -RT_ERROR; + struct irq_traps *traps; + + rt_list_for_each_entry(traps, &_traps_nodes, list) + { + if (traps->handler(traps->data)) + { + err = RT_EOK; + + break; + } + } + + return err; +} + +rt_err_t rt_pic_handle_isr(struct rt_pic_irq *pirq) +{ + rt_err_t err; + rt_list_t *handler_nodes; + struct rt_irq_desc *action; + + RT_ASSERT(pirq != RT_NULL); + RT_ASSERT(pirq->pic != RT_NULL); + + /* Corrected irq affinity */ + rt_bitmap_set_bit(pirq->affinity, rt_hw_cpu_id()); + + handler_nodes = &pirq->isr.list; + action = &pirq->isr.action; + + if (action->handler) + { + action->handler(pirq->irq, action->param); + #ifdef RT_USING_INTERRUPT_INFO + action->counter++; + #endif + + if (!rt_list_isempty(handler_nodes)) + { + struct rt_pic_isr *isr; + + rt_list_for_each_entry(isr, handler_nodes, list) + { + action = &isr->action; + + RT_ASSERT(action->handler != RT_NULL); + + action->handler(pirq->irq, action->param); + #ifdef RT_USING_INTERRUPT_INFO + action->counter++; + #endif + } + } + } + else + { + err = -RT_EEMPTY; + } + + return err; +} + +rt_weak rt_err_t rt_pic_user_extends(struct rt_pic *pic) +{ + return -RT_ENOSYS; +} + +rt_err_t rt_pic_irq_init(void) +{ + rt_err_t err = RT_EOK; + struct rt_pic *pic; + + rt_list_for_each_entry(pic, &_pic_nodes, list) + { + if (pic->ops->irq_init) + { + err = pic->ops->irq_init(pic); + + if (err) + { + LOG_E("PIC = %s init fail", pic->ops->name); + break; + } + } + } + + return err; +} + +rt_err_t rt_pic_irq_finit(void) +{ + rt_err_t err = RT_EOK; + struct rt_pic *pic; + + rt_list_for_each_entry(pic, &_pic_nodes, list) + { + if (pic->ops->irq_finit) + { + err = pic->ops->irq_finit(pic); + + if (err) + { + LOG_E("PIC = %s finit fail", pic->ops->name); + break; + } + } + } + + return err; +} + +#define _irq_call_helper(irq, fn) \ +({ \ + struct rt_pic_irq *pirq; \ + if ((pirq = irq2pirq(irq))) \ + { \ + rt_spin_lock(&pirq->rw_lock); \ + if (pirq->pic->ops->fn) \ + pirq->pic->ops->fn(pirq); \ + rt_spin_unlock(&pirq->rw_lock); \ + } \ +}) + +void rt_pic_irq_enable(int irq) +{ + _irq_call_helper(irq, irq_enable); +} + +void rt_pic_irq_disable(int irq) +{ + _irq_call_helper(irq, irq_disable); +} + +void rt_pic_irq_ack(int irq) +{ + _irq_call_helper(irq, irq_ack); +} + +void rt_pic_irq_mask(int irq) +{ + _irq_call_helper(irq, irq_mask); +} + +void rt_pic_irq_unmask(int irq) +{ + _irq_call_helper(irq, irq_unmask); +} + +void rt_pic_irq_eoi(int irq) +{ + _irq_call_helper(irq, irq_eoi); +} + +#undef _irq_call_helper + +rt_err_t rt_pic_irq_set_priority(int irq, rt_uint32_t priority) +{ + rt_err_t err = -RT_EINVAL; + struct rt_pic_irq *pirq = irq2pirq(irq); + + if (pirq) + { + rt_spin_lock(&pirq->rw_lock); + + if (pirq->pic->ops->irq_set_priority) + { + err = pirq->pic->ops->irq_set_priority(pirq, priority); + + if (!err) + { + pirq->priority = priority; + } + } + else + { + err = -RT_ENOSYS; + } + + rt_spin_unlock(&pirq->rw_lock); + } + + return err; +} + +rt_uint32_t rt_pic_irq_get_priority(int irq) +{ + rt_uint32_t priority = RT_UINT32_MAX; + struct rt_pic_irq *pirq = irq2pirq(irq); + + if (pirq) + { + rt_spin_lock(&pirq->rw_lock); + + priority = pirq->priority; + + rt_spin_unlock(&pirq->rw_lock); + } + + return priority; +} + +rt_err_t rt_pic_irq_set_affinity(int irq, rt_bitmap_t *affinity) +{ + rt_err_t err = -RT_EINVAL; + struct rt_pic_irq *pirq; + + if (affinity && (pirq = irq2pirq(irq))) + { + rt_spin_lock(&pirq->rw_lock); + + if (pirq->pic->ops->irq_set_affinity) + { + err = pirq->pic->ops->irq_set_affinity(pirq, affinity); + + if (!err) + { + rt_memcpy(pirq->affinity, affinity, sizeof(pirq->affinity)); + } + } + else + { + err = -RT_ENOSYS; + } + + rt_spin_unlock(&pirq->rw_lock); + } + + return err; +} + +rt_err_t rt_pic_irq_get_affinity(int irq, rt_bitmap_t *out_affinity) +{ + rt_err_t err = -RT_EINVAL; + struct rt_pic_irq *pirq; + + if (out_affinity && (pirq = irq2pirq(irq))) + { + rt_spin_lock(&pirq->rw_lock); + + rt_memcpy(out_affinity, pirq->affinity, sizeof(pirq->affinity)); + err = RT_EOK; + + rt_spin_unlock(&pirq->rw_lock); + } + + return err; +} + +rt_err_t rt_pic_irq_set_triger_mode(int irq, rt_uint32_t mode) +{ + rt_err_t err = -RT_EINVAL; + struct rt_pic_irq *pirq; + + if ((~mode & RT_IRQ_MODE_MASK) && (pirq = irq2pirq(irq))) + { + rt_spin_lock(&pirq->rw_lock); + + if (pirq->pic->ops->irq_set_triger_mode) + { + err = pirq->pic->ops->irq_set_triger_mode(pirq, mode); + + if (!err) + { + pirq->mode = mode; + } + } + else + { + err = -RT_ENOSYS; + } + + rt_spin_unlock(&pirq->rw_lock); + } + + return err; +} + +rt_uint32_t rt_pic_irq_get_triger_mode(int irq) +{ + rt_uint32_t mode = RT_UINT32_MAX; + struct rt_pic_irq *pirq = irq2pirq(irq); + + if (pirq) + { + rt_spin_lock(&pirq->rw_lock); + + mode = pirq->mode; + + rt_spin_unlock(&pirq->rw_lock); + } + + return mode; +} + +void rt_pic_irq_send_ipi(int irq, rt_bitmap_t *cpumask) +{ + struct rt_pic_irq *pirq; + + if (cpumask && (pirq = irq2pirq(irq))) + { + rt_hw_spin_lock(&pirq->rw_lock.lock); + + if (pirq->pic->ops->irq_send_ipi) + { + pirq->pic->ops->irq_send_ipi(pirq, cpumask); + } + + rt_hw_spin_unlock(&pirq->rw_lock.lock); + } +} + +#define _pirq_parent_call_helper(ppic, pirq, fn, ret,...) \ +({ \ + if (ppic && pirq) \ + { \ + rt_spin_lock(&pirq->rw_lock); \ + if (ppic->ops->fn) \ + { \ + struct rt_pic *cpic; \ + cpic = pirq->pic; /* push old pic */ \ + pirq->pic = ppic; \ + ret ppic->ops->fn(pirq __VA_ARGS__); \ + pirq->pic = cpic; /* pop old pic */ \ + } \ + rt_spin_unlock(&pirq->rw_lock); \ + } \ +}) + +void rt_pic_irq_parent_enable(struct rt_pic *ppic, struct rt_pic_irq *pirq) +{ + _pirq_parent_call_helper(ppic, pirq, irq_enable,,); +} + +void rt_pic_irq_parent_disable(struct rt_pic *ppic, struct rt_pic_irq *pirq) +{ + _pirq_parent_call_helper(ppic, pirq, irq_disable,,); +} + +void rt_pic_irq_parent_ack(struct rt_pic *ppic, struct rt_pic_irq *pirq) +{ + _pirq_parent_call_helper(ppic, pirq, irq_ack,,); +} + +void rt_pic_irq_parent_mask(struct rt_pic *ppic, struct rt_pic_irq *pirq) +{ + _pirq_parent_call_helper(ppic, pirq, irq_mask,,); +} + +void rt_pic_irq_parent_unmask(struct rt_pic *ppic, struct rt_pic_irq *pirq) +{ + _pirq_parent_call_helper(ppic, pirq, irq_unmask,,); +} + +void rt_pic_irq_parent_eoi(struct rt_pic *ppic, struct rt_pic_irq *pirq) +{ + _pirq_parent_call_helper(ppic, pirq, irq_eoi,,); +} + +rt_err_t rt_pic_irq_parent_set_priority(struct rt_pic *ppic, struct rt_pic_irq *pirq, rt_uint32_t priority) +{ + rt_err_t err = -RT_ENOSYS; + + _pirq_parent_call_helper(ppic, pirq, irq_set_priority, err = , ,priority); + + return err; +} + +rt_err_t rt_pic_irq_parent_set_affinity(struct rt_pic *ppic, struct rt_pic_irq *pirq, rt_bitmap_t *affinity) +{ + rt_err_t err = -RT_ENOSYS; + + _pirq_parent_call_helper(ppic, pirq, irq_set_affinity, err = , ,affinity); + + return err; +} + +rt_err_t rt_pic_irq_parent_set_triger_mode(struct rt_pic *ppic, struct rt_pic_irq *pirq, rt_uint32_t mode) +{ + rt_err_t err = -RT_ENOSYS; + + _pirq_parent_call_helper(ppic, pirq, irq_set_triger_mode, err = , ,mode); + + return err; +} + +#undef _pirq_parent_call_helper + +#ifdef RT_USING_OFW +RT_OFW_STUB_RANGE_EXPORT(pic, _pic_ofw_start, _pic_ofw_end); + +static rt_err_t ofw_pic_init(void) +{ + struct rt_ofw_node *ic_np; + + rt_ofw_foreach_node_by_prop(ic_np, "interrupt-controller") + { + rt_ofw_stub_probe_range(ic_np, &_pic_ofw_start, &_pic_ofw_end); + } + + return RT_EOK; +} +#else +static rt_err_t ofw_pic_init(void) +{ + return RT_EOK; +} +#endif /* !RT_USING_OFW */ + +rt_err_t rt_pic_init(void) +{ + rt_err_t err; + + LOG_D("init start"); + + err = ofw_pic_init(); + + LOG_D("init end"); + + return err; +} + +#if defined(RT_USING_CONSOLE) && defined(RT_USING_MSH) +static int list_irq(int argc, char**argv) +{ + rt_ubase_t level; + rt_size_t irq_nr = 0; + rt_bool_t dump_all = RT_FALSE; + const char *const irq_modes[] = + { + [RT_IRQ_MODE_NONE] = "None", + [RT_IRQ_MODE_EDGE_RISING] = "Edge-Rising", + [RT_IRQ_MODE_EDGE_FALLING] = "Edge-Falling", + [RT_IRQ_MODE_EDGE_BOTH] = "Edge-Both", + [RT_IRQ_MODE_LEVEL_HIGH] = "Level-High", + [RT_IRQ_MODE_LEVEL_LOW] = "Level-Low", + }; + static char info[RT_CONSOLEBUF_SIZE]; +#ifdef RT_USING_SMP + static char cpumask[RT_CPUS_NR + 1] = { [RT_CPUS_NR] = '\0' }; +#endif + + if (argc > 1) + { + if (!rt_strcmp(argv[1], "all")) + { + dump_all = RT_TRUE; + } + } + + level = rt_hw_interrupt_disable(); + + rt_kprintf("%-*.s %-*.s %s %-*.s %-*.s %-*.s %-*.sUsers%s\n", + 10, "IRQ", + 10, "HW-IRQ", + "MSI", + _pic_name_max, "PIC", + 12, "Mode", + #ifdef RT_USING_SMP + RT_CPUS_NR, "CPUs", + #else + 0, 0, + #endif + #ifdef RT_USING_INTERRUPT_INFO + 11, "Count", + "" + #else + 0, 0, + "-Number" + #endif + ); + + for (int i = 0; i < RT_ARRAY_SIZE(_pirq_hash); ++i) + { + struct rt_pic_irq *pirq = &_pirq_hash[i]; + + if (!pirq->pic || !(dump_all || pirq->isr.action.handler)) + { + continue; + } + + rt_snprintf(info, sizeof(info), "%-10d %-10d %c %-*.s %-*.s ", + pirq->irq, + pirq->hwirq, + pirq->msi_desc ? 'Y' : 'N', + _pic_name_max, pirq->pic->ops->name, + 12, irq_modes[pirq->mode]); + + #ifdef RT_USING_SMP + for (int group = 0, id = 0; group < RT_ARRAY_SIZE(pirq->affinity); ++group) + { + rt_bitmap_t mask = pirq->affinity[group]; + + for (int idx = 0; id < RT_CPUS_NR && idx < BITMAP_BIT_LEN(1); ++idx, ++id) + { + cpumask[RT_ARRAY_SIZE(cpumask) - id - 2] = '0' + ((mask >> idx) & 1); + } + } + #endif /* RT_USING_SMP */ + + rt_kputs(info); + #ifdef RT_USING_SMP + rt_kputs(cpumask); + #endif + + #ifdef RT_USING_INTERRUPT_INFO + rt_kprintf(" %-10d ", pirq->isr.action.counter); + rt_kputs(pirq->isr.action.name); + rt_kputs("\n"); + + if (!rt_list_isempty(&pirq->isr.list)) + { + struct rt_pic_isr *repeat_isr; + + rt_list_for_each_entry(repeat_isr, &pirq->isr.list, list) + { + rt_kputs(info); + #ifdef RT_USING_SMP + rt_kputs(cpumask); + #endif + rt_kprintf("%-10d ", repeat_isr->action.counter); + rt_kputs(repeat_isr->action.name); + rt_kputs("\n"); + } + } + #else + rt_kprintf(" %d\n", rt_list_len(&pirq->isr.list)); + #endif + + ++irq_nr; + } + + rt_hw_interrupt_enable(level); + + rt_kprintf("%d IRQs found\n", irq_nr); + + return 0; +} +MSH_CMD_EXPORT(list_irq, dump using or args = all of irq information); +#endif /* RT_USING_CONSOLE && RT_USING_MSH */ diff --git a/components/utilities/Kconfig b/components/utilities/Kconfig index 93afcc01b0..a1597db9b5 100644 --- a/components/utilities/Kconfig +++ b/components/utilities/Kconfig @@ -216,15 +216,11 @@ config RT_USING_VAR_EXPORT bool "Enable Var Export" default n -config RT_USING_ADT - bool "Enable ADT(abstract data type), such as AVL tree" - default y if ARCH_MM_MMU - default n - config RT_USING_RESOURCE_ID bool "Enable resource id" default n +source "$RTT_DIR/components/utilities/libadt/Kconfig" source "$RTT_DIR/components/utilities/rt-link/Kconfig" endmenu