添加rtthread相关代码
This commit is contained in:
		
							
								
								
									
										22
									
								
								riscv/rtthread/components/drivers/ofw/Kconfig
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										22
									
								
								riscv/rtthread/components/drivers/ofw/Kconfig
									
									
									
									
									
										Executable file
									
								
							| @@ -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 | ||||
							
								
								
									
										25
									
								
								riscv/rtthread/components/drivers/ofw/SConscript
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										25
									
								
								riscv/rtthread/components/drivers/ofw/SConscript
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| 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') | ||||
|  | ||||
| if GetDepend('RT_USING_PIC') == False: | ||||
|     SrcRemove(src, ['irq.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') | ||||
							
								
								
									
										1849
									
								
								riscv/rtthread/components/drivers/ofw/base.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										1849
									
								
								riscv/rtthread/components/drivers/ofw/base.c
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1115
									
								
								riscv/rtthread/components/drivers/ofw/fdt.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										1115
									
								
								riscv/rtthread/components/drivers/ofw/fdt.c
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										436
									
								
								riscv/rtthread/components/drivers/ofw/io.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										436
									
								
								riscv/rtthread/components/drivers/ofw/io.c
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,436 @@ | ||||
| /* | ||||
|  * 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 <rtthread.h> | ||||
|  | ||||
| #include <drivers/ofw.h> | ||||
| #include <drivers/ofw_io.h> | ||||
| #include <drivers/ofw_fdt.h> | ||||
|  | ||||
| #define DBG_TAG "rtdm.ofw" | ||||
| #define DBG_LVL DBG_INFO | ||||
| #include <rtdbg.h> | ||||
|  | ||||
| #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_EEMPTY; | ||||
|     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; | ||||
| } | ||||
|  | ||||
| #ifdef ARCH_CPU_64BIT | ||||
| #define ofw_address_cpu_cast(np, address) (void *)(address) | ||||
| #else | ||||
| #define ofw_address_cpu_cast(np, address)                       \ | ||||
| ({                                                              \ | ||||
|     if (((address) >> 32))                                      \ | ||||
|     {                                                           \ | ||||
|         LOG_W("%s find 64 bits address = %x%x",                 \ | ||||
|                 rt_ofw_node_full_name(np),                      \ | ||||
|                 ofw_static_cast(rt_ubase_t, (address) >> 32),   \ | ||||
|                 ofw_static_cast(rt_ubase_t, (address)));        \ | ||||
|     }                                                           \ | ||||
|     (void *)ofw_static_cast(rt_ubase_t, (address));             \ | ||||
| }) | ||||
| #endif | ||||
|  | ||||
| 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(ofw_address_cpu_cast(np, 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(ofw_address_cpu_cast(np, regs[0]), (size_t)regs[1]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return iomem; | ||||
| } | ||||
							
								
								
									
										670
									
								
								riscv/rtthread/components/drivers/ofw/irq.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										670
									
								
								riscv/rtthread/components/drivers/ofw/irq.c
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,670 @@ | ||||
| /* | ||||
|  * 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 <rtthread.h> | ||||
|  | ||||
| #include <drivers/pic.h> | ||||
| #include <drivers/ofw.h> | ||||
| #include <drivers/ofw_io.h> | ||||
| #include <drivers/ofw_irq.h> | ||||
|  | ||||
| #define DBG_TAG "rtdm.ofw" | ||||
| #define DBG_LVL DBG_INFO | ||||
| #include <rtdbg.h> | ||||
|  | ||||
| #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 <phandle> 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, almost no SoC will be use multi IC to implement 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 <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>. | ||||
|      */ | ||||
|  | ||||
|     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; | ||||
|         } | ||||
|  | ||||
|         map_len = sizeof(fdt32_t); | ||||
|         map_mask_len = sizeof(fdt32_t); | ||||
|  | ||||
|         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 = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>; | ||||
|          *      }; | ||||
|          */ | ||||
|  | ||||
|         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) | ||||
| { | ||||
|     for (np = rt_ofw_node_get(np); np; np = rt_ofw_get_next_parent(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_pic_dynamic_cast(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) | ||||
|         { | ||||
|             rt_phandle cpu_phandle; | ||||
|  | ||||
|             irq = ofw_map_irq(&irq_args); | ||||
|  | ||||
|             if (irq >= 0 && !rt_ofw_prop_read_u32_index(np, "interrupt-affinity", index, &cpu_phandle)) | ||||
|             { | ||||
|                 rt_uint64_t cpuid = rt_ofw_get_cpu_id(rt_ofw_find_node_by_phandle(cpu_phandle)); | ||||
|  | ||||
|                 if ((rt_int64_t)cpuid >= 0) | ||||
|                 { | ||||
|                     RT_BITMAP_DECLARE(affinity, RT_CPUS_NR) = { 0 }; | ||||
|  | ||||
|                     rt_bitmap_set_bit(affinity, cpuid); | ||||
|  | ||||
|                     if (rt_pic_irq_set_affinity(irq, affinity) == -RT_ENOSYS) | ||||
|                     { | ||||
|                         LOG_W("%s irq affinity init fail", np->full_name); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     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; | ||||
| } | ||||
							
								
								
									
										10
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/SConscript
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										10
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/SConscript
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| from building import * | ||||
|  | ||||
| cwd     = GetCurrentDir() | ||||
| src     = Glob('*.c') | ||||
|  | ||||
| CPPPATH = [cwd] | ||||
|  | ||||
| group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) | ||||
|  | ||||
| Return('group') | ||||
							
								
								
									
										339
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										339
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt.c
									
									
									
									
									
										Executable file
									
								
							| @@ -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 <fdt.h> | ||||
| #include <libfdt.h> | ||||
|  | ||||
| #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; | ||||
| } | ||||
							
								
								
									
										66
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										66
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt.h
									
									
									
									
									
										Executable file
									
								
							| @@ -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 */ | ||||
							
								
								
									
										101
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt_addresses.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										101
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt_addresses.c
									
									
									
									
									
										Executable file
									
								
							| @@ -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 <david@gibson.dropbear.id.au> | ||||
|  * Copyright (C) 2018 embedded brains GmbH | ||||
|  */ | ||||
| #include "libfdt_env.h" | ||||
|  | ||||
| #include <fdt.h> | ||||
| #include <libfdt.h> | ||||
|  | ||||
| #include "libfdt_internal.h" | ||||
|  | ||||
| static int fdt_cells(const void *fdt, int nodeoffset, const char *name) | ||||
| { | ||||
|     const fdt32_t *c; | ||||
|     uint32_t val; | ||||
|     int len; | ||||
|  | ||||
|     c = fdt_getprop(fdt, nodeoffset, name, &len); | ||||
|     if (!c) | ||||
|         return len; | ||||
|  | ||||
|     if (len != sizeof(*c)) | ||||
|         return -FDT_ERR_BADNCELLS; | ||||
|  | ||||
|     val = fdt32_to_cpu(*c); | ||||
|     if (val > FDT_MAX_NCELLS) | ||||
|         return -FDT_ERR_BADNCELLS; | ||||
|  | ||||
|     return (int)val; | ||||
| } | ||||
|  | ||||
| int fdt_address_cells(const void *fdt, int nodeoffset) | ||||
| { | ||||
|     int val; | ||||
|  | ||||
|     val = fdt_cells(fdt, nodeoffset, "#address-cells"); | ||||
|     if (val == 0) | ||||
|         return -FDT_ERR_BADNCELLS; | ||||
|     if (val == -FDT_ERR_NOTFOUND) | ||||
|         return 2; | ||||
|     return val; | ||||
| } | ||||
|  | ||||
| int fdt_size_cells(const void *fdt, int nodeoffset) | ||||
| { | ||||
|     int val; | ||||
|  | ||||
|     val = fdt_cells(fdt, nodeoffset, "#size-cells"); | ||||
|     if (val == -FDT_ERR_NOTFOUND) | ||||
|         return 1; | ||||
|     return val; | ||||
| } | ||||
|  | ||||
| /* This function assumes that [address|size]_cells is 1 or 2 */ | ||||
| int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset, | ||||
|                  const char *name, uint64_t addr, uint64_t size) | ||||
| { | ||||
|     int addr_cells, size_cells, ret; | ||||
|     uint8_t data[sizeof(fdt64_t) * 2], *prop; | ||||
|  | ||||
|     ret = fdt_address_cells(fdt, parent); | ||||
|     if (ret < 0) | ||||
|         return ret; | ||||
|     addr_cells = ret; | ||||
|  | ||||
|     ret = fdt_size_cells(fdt, parent); | ||||
|     if (ret < 0) | ||||
|         return ret; | ||||
|     size_cells = ret; | ||||
|  | ||||
|     /* check validity of address */ | ||||
|     prop = data; | ||||
|     if (addr_cells == 1) { | ||||
|         if ((addr > UINT32_MAX) || (((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)); | ||||
| } | ||||
							
								
								
									
										38
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt_empty_tree.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										38
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt_empty_tree.c
									
									
									
									
									
										Executable file
									
								
							| @@ -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 <fdt.h> | ||||
| #include <libfdt.h> | ||||
|  | ||||
| #include "libfdt_internal.h" | ||||
|  | ||||
| int fdt_create_empty_tree(void *buf, int bufsize) | ||||
| { | ||||
|     int err; | ||||
|  | ||||
|     err = fdt_create(buf, bufsize); | ||||
|     if (err) | ||||
|         return err; | ||||
|  | ||||
|     err = fdt_finish_reservemap(buf); | ||||
|     if (err) | ||||
|         return err; | ||||
|  | ||||
|     err = fdt_begin_node(buf, ""); | ||||
|     if (err) | ||||
|         return err; | ||||
|  | ||||
|     err =  fdt_end_node(buf); | ||||
|     if (err) | ||||
|         return err; | ||||
|  | ||||
|     err = fdt_finish(buf); | ||||
|     if (err) | ||||
|         return err; | ||||
|  | ||||
|     return fdt_open_into(buf, buf, bufsize); | ||||
| } | ||||
							
								
								
									
										867
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt_overlay.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										867
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt_overlay.c
									
									
									
									
									
										Executable file
									
								
							| @@ -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 <fdt.h> | ||||
| #include <libfdt.h> | ||||
|  | ||||
| #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)) { | ||||
|             /* /<fragment-name>/__overlay__/<relative-subnode-path> */ | ||||
|             rel_path = s + len; | ||||
|             rel_path_len = e - rel_path - 1; | ||||
|         } else if ((e - s) == len | ||||
|                && (memcmp(s, "/__overlay__", len - 1) == 0)) { | ||||
|             /* /<fragment-name>/__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; | ||||
| } | ||||
							
								
								
									
										859
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt_ro.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										859
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt_ro.c
									
									
									
									
									
										Executable file
									
								
							| @@ -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 <fdt.h> | ||||
| #include <libfdt.h> | ||||
|  | ||||
| #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() */ | ||||
| } | ||||
							
								
								
									
										500
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt_rw.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										500
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt_rw.c
									
									
									
									
									
										Executable file
									
								
							| @@ -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 <fdt.h> | ||||
| #include <libfdt.h> | ||||
|  | ||||
| #include "libfdt_internal.h" | ||||
|  | ||||
| static int fdt_blocks_misordered_(const void *fdt, | ||||
|                   int mem_rsv_size, int struct_size) | ||||
| { | ||||
|     return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8)) | ||||
|         || (fdt_off_dt_struct(fdt) < | ||||
|             (fdt_off_mem_rsvmap(fdt) + mem_rsv_size)) | ||||
|         || (fdt_off_dt_strings(fdt) < | ||||
|             (fdt_off_dt_struct(fdt) + struct_size)) | ||||
|         || (fdt_totalsize(fdt) < | ||||
|             (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); | ||||
| } | ||||
|  | ||||
| static int fdt_rw_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; | ||||
| } | ||||
							
								
								
									
										60
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt_strerror.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										60
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt_strerror.c
									
									
									
									
									
										Executable file
									
								
							| @@ -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 <fdt.h> | ||||
| #include <libfdt.h> | ||||
|  | ||||
| #include "libfdt_internal.h" | ||||
|  | ||||
| struct fdt_errtabent { | ||||
|     const char *str; | ||||
| }; | ||||
|  | ||||
| #define FDT_ERRTABENT(val) \ | ||||
|     [(val)] = { .str = #val, } | ||||
|  | ||||
| static struct fdt_errtabent fdt_errtable[] = { | ||||
|     FDT_ERRTABENT(FDT_ERR_NOTFOUND), | ||||
|     FDT_ERRTABENT(FDT_ERR_EXISTS), | ||||
|     FDT_ERRTABENT(FDT_ERR_NOSPACE), | ||||
|  | ||||
|     FDT_ERRTABENT(FDT_ERR_BADOFFSET), | ||||
|     FDT_ERRTABENT(FDT_ERR_BADPATH), | ||||
|     FDT_ERRTABENT(FDT_ERR_BADPHANDLE), | ||||
|     FDT_ERRTABENT(FDT_ERR_BADSTATE), | ||||
|  | ||||
|     FDT_ERRTABENT(FDT_ERR_TRUNCATED), | ||||
|     FDT_ERRTABENT(FDT_ERR_BADMAGIC), | ||||
|     FDT_ERRTABENT(FDT_ERR_BADVERSION), | ||||
|     FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE), | ||||
|     FDT_ERRTABENT(FDT_ERR_BADLAYOUT), | ||||
|     FDT_ERRTABENT(FDT_ERR_INTERNAL), | ||||
|     FDT_ERRTABENT(FDT_ERR_BADNCELLS), | ||||
|     FDT_ERRTABENT(FDT_ERR_BADVALUE), | ||||
|     FDT_ERRTABENT(FDT_ERR_BADOVERLAY), | ||||
|     FDT_ERRTABENT(FDT_ERR_NOPHANDLES), | ||||
|     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 "<valid offset/length>"; | ||||
|     else if (errval == 0) | ||||
|         return "<no error>"; | ||||
|     else if (-errval < FDT_ERRTABSIZE) { | ||||
|         const char *s = fdt_errtable[-errval].str; | ||||
|  | ||||
|         if (s) | ||||
|             return s; | ||||
|     } | ||||
|  | ||||
|     return "<unknown error>"; | ||||
| } | ||||
							
								
								
									
										384
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt_sw.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										384
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt_sw.c
									
									
									
									
									
										Executable file
									
								
							| @@ -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 <fdt.h> | ||||
| #include <libfdt.h> | ||||
|  | ||||
| #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; | ||||
| } | ||||
							
								
								
									
										94
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt_wip.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										94
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/fdt_wip.c
									
									
									
									
									
										Executable file
									
								
							| @@ -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 <fdt.h> | ||||
| #include <libfdt.h> | ||||
|  | ||||
| #include "libfdt_internal.h" | ||||
|  | ||||
| int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, | ||||
|                     const char *name, int namelen, | ||||
|                     uint32_t idx, const void *val, | ||||
|                     int len) | ||||
| { | ||||
|     void *propval; | ||||
|     int proplen; | ||||
|  | ||||
|     propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen, | ||||
|                     &proplen); | ||||
|     if (!propval) | ||||
|         return proplen; | ||||
|  | ||||
|     if ((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; | ||||
| } | ||||
							
								
								
									
										2154
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/libfdt.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										2154
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/libfdt.h
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										100
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/libfdt_env.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										100
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/libfdt_env.h
									
									
									
									
									
										Executable file
									
								
							| @@ -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 <stdbool.h> | ||||
| #include <stddef.h> | ||||
| #include <stdint.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <limits.h> | ||||
|  | ||||
| #ifdef __CHECKER__ | ||||
| #define FDT_FORCE __attribute__((force)) | ||||
| #define FDT_BITWISE __attribute__((bitwise)) | ||||
| #else | ||||
| #define FDT_FORCE | ||||
| #define FDT_BITWISE | ||||
| #endif | ||||
|  | ||||
| #include <rtthread.h> | ||||
|  | ||||
| #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 <AvailabilityMacros.h> | ||||
|  | ||||
| /* 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 */ | ||||
							
								
								
									
										192
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/libfdt_internal.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										192
									
								
								riscv/rtthread/components/drivers/ofw/libfdt/libfdt_internal.h
									
									
									
									
									
										Executable file
									
								
							| @@ -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 <fdt.h> | ||||
|  | ||||
| #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 */ | ||||
							
								
								
									
										625
									
								
								riscv/rtthread/components/drivers/ofw/ofw.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										625
									
								
								riscv/rtthread/components/drivers/ofw/ofw.c
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,625 @@ | ||||
| /* | ||||
|  * Copyright (c) 2006-2024, RT-Thread Development Team | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  * | ||||
|  * Change Logs: | ||||
|  * Date           Author       Notes | ||||
|  * 2022-08-25     GuEe-GUI     first version | ||||
|  */ | ||||
|  | ||||
| #include <rtthread.h> | ||||
| #include <rtdevice.h> | ||||
| #include <drivers/platform.h> | ||||
| #include <drivers/core/bus.h> | ||||
| #include "../serial/serial_dm.h" | ||||
|  | ||||
| #define DBG_TAG "rtdm.ofw" | ||||
| #define DBG_LVL DBG_INFO | ||||
| #include <rtdbg.h> | ||||
|  | ||||
| #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 = RT_NULL; | ||||
|     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 int tty_device_compare(rt_device_t dev, void *data) | ||||
| { | ||||
|     rt_ubase_t *args_list; | ||||
|     char *dst_con; | ||||
|     const char *console, **ofw_name; | ||||
|     const struct rt_ofw_node_id *id; | ||||
|     struct rt_platform_device *pdev; | ||||
|     int *tty_idx, tty_id, tty_name_len; | ||||
|  | ||||
|     pdev = rt_container_of(dev, struct rt_platform_device, parent); | ||||
|     id = pdev->id; | ||||
|  | ||||
|     args_list = data; | ||||
|     tty_idx = (int *)args_list[0]; | ||||
|     tty_id = args_list[1]; | ||||
|     console = (const char *)args_list[2]; | ||||
|     tty_name_len = args_list[3]; | ||||
|     dst_con = (char *)args_list[4]; | ||||
|     ofw_name = (const char **)args_list[5]; | ||||
|  | ||||
|     if (id && id->type[0] && !strncmp(id->type, console, tty_name_len)) | ||||
|     { | ||||
|         if (*tty_idx == tty_id) | ||||
|         { | ||||
|             *ofw_name = ofw_console_serial_find(dst_con, pdev->parent.ofw_node); | ||||
|  | ||||
|             return RT_EOK; | ||||
|         } | ||||
|  | ||||
|         ++*tty_idx; | ||||
|     } | ||||
|  | ||||
|     return -RT_EEMPTY; | ||||
| } | ||||
|  | ||||
| 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_ubase_t args_list[6]; | ||||
|         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; | ||||
|         } | ||||
|  | ||||
|         args_list[0] = (rt_ubase_t)&tty_idx; | ||||
|         args_list[1] = tty_id; | ||||
|         args_list[2] = (rt_ubase_t)console; | ||||
|         args_list[3] = tty_name_len; | ||||
|         args_list[4] = (rt_ubase_t)dst_con; | ||||
|         args_list[5] = (rt_ubase_t)&ofw_name; | ||||
|         rt_bus_for_each_dev(platform_bus, &args_list, tty_device_compare); | ||||
|     } | ||||
|  | ||||
|     return ofw_name; | ||||
| } | ||||
|  | ||||
| rt_err_t rt_ofw_console_setup(void) | ||||
| { | ||||
|     rt_err_t err = -RT_ENOSYS; | ||||
|     char con_name[RT_NAME_MAX], *options = RT_NULL; | ||||
|     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) | ||||
|             { | ||||
|                 const char *ch = con; | ||||
|  | ||||
|                 while (*ch && *ch != ' ') | ||||
|                 { | ||||
|                     if (*ch++ == ',') | ||||
|                     { | ||||
|                         options = (char *)ch; | ||||
|  | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 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); | ||||
|  | ||||
|     if (options) | ||||
|     { | ||||
|         rt_device_t con_dev = rt_console_get_device(); | ||||
|  | ||||
|         if (con_dev) | ||||
|         { | ||||
|             struct serial_configure con_conf = serial_cfg_from_args(options); | ||||
|  | ||||
|             rt_device_control(con_dev, RT_DEVICE_CTRL_CONFIG, &con_conf); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     rt_fdt_earlycon_kick(FDT_EARLYCON_KICK_COMPLETED); | ||||
|  | ||||
|     LOG_I("Console: %s (%s)", con, ofw_name ? ofw_name : "<unknown>"); | ||||
|  | ||||
|     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"); | ||||
|  | ||||
|             if (!sibling_too && org_np == np) | ||||
|             { | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             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 */ | ||||
							
								
								
									
										74
									
								
								riscv/rtthread/components/drivers/ofw/ofw_internal.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										74
									
								
								riscv/rtthread/components/drivers/ofw/ofw_internal.h
									
									
									
									
									
										Executable file
									
								
							| @@ -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 <rtthread.h> | ||||
| #include <posix/string.h> | ||||
| #include <drivers/ofw.h> | ||||
|  | ||||
| #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); | ||||
| int ofw_alias_node_id(struct rt_ofw_node *np); | ||||
| rt_err_t ofw_phandle_hash_reset(rt_phandle min, rt_phandle max); | ||||
|  | ||||
| #endif /* __OFW_INTERNAL_H__ */ | ||||
							
								
								
									
										197
									
								
								riscv/rtthread/components/drivers/ofw/raw.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										197
									
								
								riscv/rtthread/components/drivers/ofw/raw.c
									
									
									
									
									
										Executable file
									
								
							| @@ -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 <drivers/ofw_raw.h> | ||||
|  | ||||
| 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; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user