diff --git a/components/esp_common/include/esp_macros.h b/components/esp_common/include/esp_macros.h index 28aa5106c2..753f095ddb 100644 --- a/components/esp_common/include/esp_macros.h +++ b/components/esp_common/include/esp_macros.h @@ -46,6 +46,35 @@ extern "C" { #define CHOOSE_MACRO_VA_ARG(MACRO_WITH_ARGS, MACRO_WITH_NO_ARGS, ...) CHOOSE_MACRO_VA_ARG_INN(0, ##__VA_ARGS__, MACRO_WITH_ARGS, MACRO_WITH_NO_ARGS, 0) #endif +/* Count number of arguments of __VA_ARGS__ + * - reference https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s + * - __GET_NTH_ARG__() takes args >= N (64) but only expand to Nth one (64th) + * - __RSEQ_N__() is reverse sequential to N to add padding to have Nth + * position is the same as the number of arguments + * - ##__VA_ARGS__ is used to deal with 0 paramerter (swallows comma) + */ +#ifndef __VA_NARG__ +# define __VA_NARG__(...) __NARG__(_0, ##__VA_ARGS__, __RSEQ_N__()) + +# define __NARG__(...) __GET_NTH_ARG__(__VA_ARGS__) +# define __GET_NTH_ARG__( \ + _01,_02,_03,_04,_05,_06,_07,_08,_09,_10, \ + _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ + _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ + _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ + _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ + _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ + _61,_62,_63,N,...) N +# define __RSEQ_N__() \ + 62,61,60, \ + 59,58,57,56,55,54,53,52,51,50, \ + 49,48,47,46,45,44,43,42,41,40, \ + 39,38,37,36,35,34,33,32,31,30, \ + 29,28,27,26,25,24,23,22,21,20, \ + 19,18,17,16,15,14,13,12,11,10, \ + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +#endif + /* test macros */ #define foo_args(...) 1 #define foo_no_args() 2 diff --git a/components/esp_hw_support/CMakeLists.txt b/components/esp_hw_support/CMakeLists.txt index 527a2ccec3..544a049a53 100644 --- a/components/esp_hw_support/CMakeLists.txt +++ b/components/esp_hw_support/CMakeLists.txt @@ -74,7 +74,8 @@ if(NOT BOOTLOADER_BUILD) endif() if(CONFIG_SOC_PAU_SUPPORTED) - list(APPEND srcs "port/pau_regdma.c") + list(APPEND srcs "port/pau_regdma.c" + "port/regdma_link.c") endif() if(CONFIG_SOC_PM_CPU_RETENTION_BY_SW) diff --git a/components/esp_hw_support/include/esp_private/esp_regdma.h b/components/esp_hw_support/include/esp_private/esp_regdma.h new file mode 100644 index 0000000000..2235d83b86 --- /dev/null +++ b/components/esp_hw_support/include/esp_private/esp_regdma.h @@ -0,0 +1,649 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "esp_assert.h" +#include "esp_macros.h" +#include "esp_err.h" +#include "esp_bit_defs.h" +#include "soc/soc_caps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if SOC_PAU_SUPPORTED +#include "hal/pau_types.h" + +#define REGDMA_LINK_DBG 0 /* Enable REGDMA link info dump apis*/ +#define REGDMA_LINK_ENTRY_NUM (PAU_REGDMA_LINK_NUM) /* Maximum number of REG DMA linked list entries */ + +#define ENTRY(n) (BIT(n)) + +#define REGDMA_PCR_LINK(_pri) ((0x01 << 8) | _pri) +#define REGDMA_MODEMSYSCON_LINK(_pri) ((0x02 << 8) | _pri) + +#define REGDMA_INTMTX_LINK(_pri) ((0x0d << 8) | _pri) +#define REGDMA_HPSYS_LINK(_pri) ((0x0e << 8) | _pri) +#define REGDMA_TEEAPM_LINK(_pri) ((0x0f << 8) | _pri) + +#define REGDMA_UART_LINK(_pri) ((0x10 << 8) | _pri) +#define REGDMA_TIMG_LINK(_pri) ((0x11 << 8) | _pri) +#define REGDMA_IOMUX_LINK(_pri) ((0x12 << 8) | _pri) +#define REGDMA_SPIMEM_LINK(_pri) ((0x13 << 8) | _pri) +#define REGDMA_SYSTIMER_LINK(_pri) ((0x14 << 8) | _pri) + +typedef enum { + REGDMA_LINK_PRI_0 = 0, + REGDMA_LINK_PRI_1, + REGDMA_LINK_PRI_2, + REGDMA_LINK_PRI_3, + REGDMA_LINK_PRI_4, + REGDMA_LINK_PRI_5, + REGDMA_LINK_PRI_6, + REGDMA_LINK_PRI_7, +} regdma_link_priority_t; + +typedef pau_regdma_link_addr_t regdma_entry_buf_t; + +typedef enum regdma_link_mode { + REGDMA_LINK_MODE_CONTINUOUS = 0, /*!< Link used to backup registers with consecutive addresses */ + REGDMA_LINK_MODE_ADDR_MAP, /*!< Link used to backup selected registers according to bitmap */ + REGDMA_LINK_MODE_WRITE, /*!< Link used to direct write to registers*/ + REGDMA_LINK_MODE_WAIT /*!< Link used to wait for register value to meet condition*/ +} regdma_link_mode_t; + + +typedef struct regdma_link_head { + volatile uint32_t length: 10, /* total count of registers that need to be backup or restore, unit: 1 word = 4 bytes */ + reserve0: 6, + mode : 4, /* mode of current link */ + reserve1: 8, + branch : 1, /* branch link flag */ + skip_r : 1, /* skip the current linked node when restore the register */ + skip_b : 1, /* skip the current linked node when backup the register */ + eof : 1; /* end of link */ +} regdma_link_head_t; + +/* Continuous type linked list node body type definition */ +typedef struct regdma_link_continuous_body { + volatile void *next; + volatile void *backup; + volatile void *restore; + volatile void *mem; +} regdma_link_continuous_body_t; + +/* Address Map type linked list node body type definition */ +typedef struct regdma_link_addr_map_body { + volatile void *next; + volatile void *backup; + volatile void *restore; + volatile void *mem; + volatile uint32_t map[4]; +} regdma_link_addr_map_body_t; + +/* Write/Wait type linked list node body type definition */ +typedef struct regdma_link_write_wait_body { + volatile void *next; + volatile void *backup; + volatile uint32_t value; + volatile uint32_t mask; +} regdma_link_write_wait_body_t; + +/* Branch Continuous type linked list node body type definition */ +typedef struct regdma_link_branch_continuous_body { + regdma_entry_buf_t next; + volatile void *backup; + volatile void *restore; + volatile void *mem; +} regdma_link_branch_continuous_body_t; + +/* Branch Address Map type linked list node body type definition */ +typedef struct regdma_link_branch_addr_map_body { + regdma_entry_buf_t next; + volatile void *backup; + volatile void *restore; + volatile void *mem; + volatile uint32_t map[4]; +} regdma_link_branch_addr_map_body_t; + +/* Branch Write/Wait type linked list node body type definition */ +typedef struct regdma_link_branch_write_wait_body { + regdma_entry_buf_t next; + volatile void *backup; + volatile uint32_t value; + volatile uint32_t mask; +} regdma_link_branch_write_wait_body_t; + +ESP_STATIC_ASSERT(REGDMA_LINK_ENTRY_NUM < 16, "regdma link entry number should less 16"); +typedef struct regdma_link_stats { + volatile uint32_t ref: REGDMA_LINK_ENTRY_NUM, /* a bitmap, identifies which entry has referenced the current link */ + reserve: 16-REGDMA_LINK_ENTRY_NUM, + id: 16; /* REGDMA linked list node unique identifier */ + volatile uint32_t module; /* a bitmap used to identify the module to which the current node belongs */ +} regdma_link_stats_t; + +typedef struct regdma_link_continuous { + regdma_link_stats_t stat; + regdma_link_head_t head; + regdma_link_continuous_body_t body; + volatile uint32_t buff[0]; +} regdma_link_continuous_t; + +typedef struct regdma_link_addr_map { + regdma_link_stats_t stat; + regdma_link_head_t head; + regdma_link_addr_map_body_t body; + volatile uint32_t buff[0]; +} regdma_link_addr_map_t; + +typedef struct regdma_link_write_wait { + regdma_link_stats_t stat; + regdma_link_head_t head; + regdma_link_write_wait_body_t body; +} regdma_link_write_wait_t; + +typedef struct regdma_link_branch_continuous { + regdma_link_stats_t stat; + regdma_link_head_t head; + regdma_link_branch_continuous_body_t body; + volatile uint32_t buff[0]; +} regdma_link_branch_continuous_t; + +typedef struct regdma_link_branch_addr_map { + regdma_link_stats_t stat; + regdma_link_head_t head; + regdma_link_branch_addr_map_body_t body; + volatile uint32_t buff[0]; +} regdma_link_branch_addr_map_t; + +typedef struct regdma_link_branch_write_wait { + regdma_link_stats_t stat; + regdma_link_head_t head; + regdma_link_branch_write_wait_body_t body; +} regdma_link_branch_write_wait_t; + +typedef struct regdma_link_config { + regdma_link_head_t head; + union { + regdma_link_continuous_body_t continuous; + regdma_link_addr_map_body_t addr_map; + regdma_link_write_wait_body_t write_wait; + }; + int id; /* REGDMA linked list node unique identifier */ +} regdma_link_config_t; + + +#define REGDMA_LINK_HEAD(plink) (((regdma_link_config_t *)plink)->head) + + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#endif + +#define REGDMA_LINK_HEAD_INIT(_l, _m, _b, _sr, _sb) \ + { \ + .length = (_l), \ + .mode = (_m), \ + .branch = (_b), \ + .skip_r = (_sr), \ + .skip_b = (_sb), \ + .eof = 0 \ + } + +#define REGDMA_LINK_CONTINUOUS_INIT(_id, _backup, _restore, _len, _skip_b, _skip_r) \ + { \ + .head = REGDMA_LINK_HEAD_INIT( \ + _len, \ + REGDMA_LINK_MODE_CONTINUOUS,\ + 0, \ + _skip_r, \ + _skip_b \ + ), \ + .continuous = { \ + .next = NULL, \ + .backup = (void *)_backup, \ + .restore = (void *)_restore, \ + .mem = NULL \ + }, \ + .id = (_id) \ + } + +#define REGDMA_LINK_ADDR_MAP_INIT(_id, _backup, _restore, _len, _skip_b, _skip_r, ...) \ + { \ + .head = REGDMA_LINK_HEAD_INIT( \ + _len, \ + REGDMA_LINK_MODE_ADDR_MAP, \ + 0, \ + _skip_r, \ + _skip_b \ + ), \ + .addr_map = { \ + .next = NULL, \ + .backup = (void *)_backup, \ + .restore = (void *)_restore, \ + .mem = NULL, \ + .map = {__VA_ARGS__} \ + }, \ + .id = (_id) \ + } + +#define REGDMA_LINK_WRITE_INIT(_id, _backup, _val, _mask, _skip_b, _skip_r) \ + { \ + .head = REGDMA_LINK_HEAD_INIT( \ + 0, \ + REGDMA_LINK_MODE_WRITE, \ + 0, \ + _skip_r, \ + _skip_b \ + ), \ + .write_wait = { \ + .next = NULL, \ + .backup = (void *)_backup, \ + .value = (_val), \ + .mask = (_mask) \ + }, \ + .id = (_id) \ + } + +#define REGDMA_LINK_WAIT_INIT(_id, _backup, _val, _mask, _skip_b, _skip_r) \ + { \ + .head = REGDMA_LINK_HEAD_INIT( \ + 0, \ + REGDMA_LINK_MODE_WAIT, \ + 0, \ + _skip_r, \ + _skip_b \ + ), \ + .write_wait = { \ + .next = NULL, \ + .backup = (void *)_backup, \ + .value = (_val), \ + .mask = (_mask) \ + }, \ + .id = (_id) \ + } + + +/** + * @brief Create a REGDMA continuous type linked list node without retention buffer and the retention buffer is passed in by the caller + * @param backup Register address to be backed up by REGDMA + * @param buff Retention buffer, it needs to be allocated by the caller and passed in by this argument + * @param len Number of registers to be backed up + * @param restore Register address to be restored by REGDMA + * @param next The next REGDMA linked list node + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_continuous(void *backup, void *buff, int len, void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a REGDMA addr_map type linked list node without retention buffer and the retention buffer is passed in by the caller + * @param backup Register address to be backed up by REGDMA + * @param buff Retention buffer, it needs to be allocated by the caller and passed in by this argument + * @param bitmap The register bitmap that needs to be backed up and restored. when the bitmap corresponding to the + * register is 1, it needs to be backed up or restored, otherwise the corresponding register is skipped. + * @param len Number of registers to be backed up + * @param restore Register address to be restored by REGDMA + * @param next The next REGDMA linked list node + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_addr_map(void *backup, void *buff, uint32_t bitmap[4], int len, void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a REGDMA write type linked list node without retention buffer and the retention buffer is passed in by the caller + * @param backup Register address to be backed up by REGDMA + * @param value The value to be written to the register + * @param mask The mask of value + * @param next The next REGDMA linked list node + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_write(void *backup, uint32_t value, uint32_t mask, void *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a REGDMA write type linked list node without retention buffer and the retention buffer is passed in by the caller + * @param backup Register address to be backed up by REGDMA + * @param value The register value that needs to be waited for + * @param mask The mask of value + * @param next The next REGDMA linked list node + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_wait(void *backup, uint32_t value, uint32_t mask, void *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a REGDMA continuouos branch list node without retention buffer and the retention buffer is passed in by the caller + * @param backup Register address to be backed up by REGDMA + * @param buff Retention buffer, it needs to be allocated by the caller and passed in by this argument + * @param len Number of registers to be backed up + * @param restore Register address to be restored by REGDMA + * @param next The next REGDMA linked list node (supports up to 4 next pointers) + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_branch_continuous(void *backup, void *buff, int len, void *restore, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a REGDMA addr_map branch list node without retention buffer and the retention buffer is passed in by the caller + * @param backup Register address to be backed up by REGDMA + * @param buff Retention buffer, it needs to be allocated by the caller and passed in by this argument + * @param bitmap The register bitmap that needs to be backed up and restored. when the bitmap corresponding to the + * register is 1, it needs to be backed up or restored, otherwise the corresponding register is skipped. + * @param len Number of registers to be backed up + * @param restore Register address to be restored by REGDMA + * @param next The next REGDMA linked list node (supports up to 4 next pointers) + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_branch_addr_map(void *backup, void *buff, uint32_t bitmap[4], int len, void *restore, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a REGDMA write branch list node without retention buffer and the retention buffer is passed in by the caller + * @param backup Register address to be backed up by REGDMA + * @param value The value to be written to the register + * @param mask The mask of value + * @param next The next REGDMA linked list node (supports up to 4 next pointers) + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_branch_write(void *backup, uint32_t value, uint32_t mask, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a REGDMA wait branch list node without retention buffer and the retention buffer is passed in by the caller + * @param backup Register address to be backed up by REGDMA + * @param value The register value that needs to be waited for + * @param mask The mask of value + * @param next The next REGDMA linked list node (supports up to 4 next pointers) + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_branch_wait(void *backup, uint32_t value, uint32_t mask, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a default REGDMA continuous type linked list node with retention buffer + * @param backup Register address to be backed up by REGDMA + * @param len Number of registers to be backed up + * @param restore Register address to be restored by REGDMA + * @param next The next REGDMA linked list node + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_continuous_default(void *backup, int len, void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a default REGDMA addr_map type linked list node with retention buffer + * @param backup Register address to be backed up by REGDMA + * @param bitmap The register bitmap that needs to be backed up and restored. when the bitmap corresponding to the + * register is 1, it needs to be backed up or restored, otherwise the corresponding register is skipped. + * @param len Number of registers to be backed up + * @param restore Register address to be restored by REGDMA + * @param next The next REGDMA linked list node + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_addr_map_default(void *backup, uint32_t bitmap[4], int len, void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a default REGDMA write type linked list node with retention buffer + * @param backup Register address to be backed up by REGDMA + * @param value The register value that needs to be waited for + * @param mask The mask of value + * @param next The next REGDMA linked list node + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_write_default(void *backup, uint32_t value, uint32_t mask, void *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a default REGDMA wait type linked list node with retention buffer + * @param backup Register address to be backed up by REGDMA + * @param value The register value that needs to be waited for + * @param mask The mask of value + * @param next The next REGDMA linked list node + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_wait_default(void *backup, uint32_t value, uint32_t mask, void *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a default REGDMA continuous branch list node with retention buffer + * @param backup Register address to be backed up by REGDMA + * @param len Number of registers to be backed up + * @param restore Register address to be restored by REGDMA + * @param next The next REGDMA linked list node (supports up to 4 next pointers) + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_branch_continuous_default(void *backup, int len, void *restore, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a default REGDMA addr_map branch list node with retention buffer + * @param backup Register address to be backed up by REGDMA + * @param bitmap The register bitmap that needs to be backed up and restored. when the bitmap corresponding to the + * register is 1, it needs to be backed up or restored, otherwise the corresponding register is skipped. + * @param len Number of registers to be backed up + * @param restore Register address to be restored by REGDMA + * @param next The next REGDMA linked list node (supports up to 4 next pointers) + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_branch_addr_map_default(void *backup, uint32_t bitmap[4], int len, void *restore, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a default REGDMA write branch list node with retention buffer + * @param backup Register address to be backed up by REGDMA + * @param value The value to be written to the register + * @param mask The mask of value + * @param next The next REGDMA linked list node (supports up to 4 next pointers) + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_branch_write_default(void *backup, uint32_t value, uint32_t mask, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a default REGDMA wait branch list node with retention buffer + * @param backup Register address to be backed up by REGDMA + * @param value The register value that needs to be waited for + * @param mask The mask of value + * @param next The next REGDMA linked list node (supports up to 4 next pointers) + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_branch_wait_default(void *backup, uint32_t value, uint32_t mask, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create and initialize a REGDMA linked list node through configuration parameters + * @param config REGDMA linked node configuration parameters + * @param branch Is it a branch node + * @param module The module identifier of the current linked list node + * @param nentry The number of next pointers + * @param args next pointer, Since REGDMA supports 4 entries, it supports up to 4 variable parameter next pointers, and more will be ignored + * @return Initialized REGDMA linked list head node pointer + */ +void *regdma_link_init(const regdma_link_config_t *config, bool branch, uint32_t module, int nentry, ...); + +/** + * @brief Recurse the REGDMA linked list and call the hook subroutine for each node + * @param link The REGDMA linkded list head pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + * @param hook Subroutines called during recursion, argument 1 is the pointer to the + * recursive node object, argument 2 is the entry to which the node belongs + * and the argument 3 is the position of the node in the current linked + * list (from head to tail, the position of the head node is 0) + * @return The REGDMA linked list node pointer indicated by the link argument + */ +void *regdma_link_recursive(void *link, int entry, void (*hook)(void *, int, int)); + +/** + * @brief Find the linked list node object by node position + * @param link The REGDMA linkded list head pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + * @param pos Node position + * @return The linked list node object pointer or NULL + */ +void *regdma_find_link_by_pos(void *link, int entry, int pos); + +/** + * @brief Find the linked list node object by node unique identifier + * @param link The REGDMA linkded list head pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + * @param id REGDMA linked list node unique identifier + * @return The linked list node object pointer or NULL + */ +void *regdma_find_link_by_id(void *link, int entry, int id); + +/** + * @brief Destroy the REGDMA linked list indicated by the entry argument + * @param link The REGDMA linkded list head pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + */ +void regdma_link_destroy(void *link, int entry); + +/** + * @brief Generate the statistics information of the REGDMA linked list indicated by the entry argument + * @param link The REGDMA linkded list head pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + */ +void regdma_link_stats(void *link, int entry); + +/** + * @brief Set the value and mask of write or wait type REGDMA linked list node + * @param link Write or wait type REGDMA linked list node pointer + * @param value The value to be written to the register + * @param mask The mask of value + */ +void regdma_link_set_write_wait_content(void *link, uint32_t value, uint32_t mask); + +/** + * @brief Print all node information of the REGDMA linked list indicated by the entry argument + * @param link The REGDMA linkded list head pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + */ +void regdma_link_show_memories(void *link, int entry); + +/** + * @brief Update REGDMA linked list node next pointers + * @param link The pointer of the REGDMA linked list node whose next field will be modified + * @param nentry The number of next pointers + */ +void regdma_link_update_next(void *link, int nentry, ...); + +/** + * @brief Get all node entry reference bitmaps from the start of the link argument to the + * end of the tail argument in the REGDMA linked list indicated by the entry argument + * @param link The REGDMA linkded list head pointer + * @param tail The REGDMA linkded list tail pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + * @return The entry reference bitmap of all nodes starting from the link argument to the end of the tail argument + */ +uint32_t regdma_link_get_owner_bitmap(void *link, void *tail, int entry); + +/** + * @brief Find the head node of the specified module in the REGDMA linked list indicated by the + * entry argument starting from the link argument to the end of the tail argument + * @param link The REGDMA linkded list head pointer + * @param tail The REGDMA linkded list tail pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + * @param module Module bitmap Identification + * @return The found head node pointer or NULL + */ +void *regdma_find_module_link_head(void *link, void *tail, int entry, uint32_t module); + +/** + * @brief Find the tail node of the specified module in the REGDMA linked list indicated by the + * entry argument starting from the link argument to the end of the tail argument + * @param link The REGDMA linkded list head pointer + * @param tail The REGDMA linkded list tail pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + * @param module Module bitmap Identification + * @return The found tail node pointer or NULL + */ +void *regdma_find_module_link_tail(void *link, void *tail, int entry, uint32_t module); + +/** + * @brief Find the tail node of the previous module of the specified module in the REGDMA linked list + * indicated by the entry argument starting from the link argment to the end of the tail argument + * @param link The REGDMA linkded list head pointer + * @param tail The REGDMA linkded list tail pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + * @param module Module bitmap Identification + * @return The found tail node pointer or NULL + */ +void *regdma_find_prev_module_link_tail(void *link, void *tail, int entry, uint32_t module); + +/** + * @brief Find the head node of the next module of the specified module in the REGDMA linked list + * indicated by the entry argument starting from the link argment to the end of the tail argument + * @param link The REGDMA linkded list head pointer + * @param tail The REGDMA linkded list tail pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + * @param module Module bitmap Identification + * @return The found head node pointer or NULL + */ +void *regdma_find_next_module_link_head(void *link, void *tail, int entry, uint32_t module); + +#define regdma_link_init_safe(pcfg, branch, module, ...) regdma_link_init((pcfg), (branch), (module), __VA_NARG__(__VA_ARGS__), ##__VA_ARGS__) + +#define regdma_link_update_next_safe(link, ...) regdma_link_update_next((link), __VA_NARG__(__VA_ARGS__), ##__VA_ARGS__) + +#endif // SOC_PAU_SUPPORTED + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hw_support/include/esp_private/regdma_link.h b/components/esp_hw_support/include/esp_private/regdma_link.h new file mode 100644 index 0000000000..2585e13233 --- /dev/null +++ b/components/esp_hw_support/include/esp_private/regdma_link.h @@ -0,0 +1,199 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __REGDMA_LINK_H__ +#define __REGDMA_LINK_H__ + +#include +#include +#include "esp_bit_defs.h" +#include "soc/soc_caps.h" + +#if SOC_PAU_SUPPORTED +#include "esp_regdma.h" + +#define FILL_PLINK_HEAD(_pl, _len, _mode, _branch, _sr, _sb, _eof) { \ + _pl->head.length = _len; \ + _pl->head.mode = _mode; \ + _pl->head.branch = _branch; \ + _pl->head.skip_r = _sr; \ + _pl->head.skip_b = _sb; \ + _pl->head.eof = _eof; \ +} + +#define FILL_PLINK_STAT(_pl, _ref, _id, _module) { \ + _pl->stat.ref = _ref; \ + _pl->stat.id = _id; \ + _pl->stat.module = _module; \ +} + +static inline void * regdma_link_init_continuous( + regdma_link_continuous_t *plink, void *buff, void *backup, int len, + void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + assert(plink != NULL); + assert(buff !=NULL); + + FILL_PLINK_HEAD(plink, len, REGDMA_LINK_MODE_CONTINUOUS, 0, skip_r, skip_b, !next); + plink->body.next = next; + plink->body.backup = backup; + plink->body.restore = restore; + plink->body.mem = buff; + FILL_PLINK_STAT(plink, 0, (uint16_t)id, module); + + return (void *)plink; +} + +static inline int regdma_link_addr_map_count(uint32_t bitmap[4]) +{ + return __builtin_popcount(bitmap[0]) + \ + __builtin_popcount(bitmap[1]) + \ + __builtin_popcount(bitmap[2]) + \ + __builtin_popcount(bitmap[3]); +} + +static inline void * regdma_link_init_addr_map( + regdma_link_addr_map_t *plink, void *buff, void *backup, uint32_t bitmap[4], + int len, void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + assert(plink != NULL); + assert(buff != NULL); + assert(len == regdma_link_addr_map_count(bitmap)); + + FILL_PLINK_HEAD(plink, len, REGDMA_LINK_MODE_ADDR_MAP, 0, skip_r, skip_b, !next); + plink->body.next = next; + plink->body.backup = backup; + plink->body.restore = restore; + plink->body.mem = buff; + plink->body.map[0] = bitmap[0]; + plink->body.map[1] = bitmap[1]; + plink->body.map[2] = bitmap[2]; + plink->body.map[3] = bitmap[3]; + FILL_PLINK_STAT(plink, 0, (uint16_t)id, module); + + return (void *)plink; +} + +static inline void * regdma_link_init_write( + regdma_link_write_wait_t *plink, void *backup, uint32_t value, + uint32_t mask, void *next, bool skip_b, bool skip_r, int id, + uint32_t module) +{ + assert(plink != NULL); + + FILL_PLINK_HEAD(plink, 0, REGDMA_LINK_MODE_WRITE, 0, skip_r, skip_b, !next); + plink->body.next = next; + plink->body.backup = backup; + plink->body.value = value; + plink->body.mask = mask; + FILL_PLINK_STAT(plink, 0, (uint16_t)id, module); + + return (void *)plink; +} + +static inline void * regdma_link_init_wait( + regdma_link_write_wait_t *plink, void *backup, uint32_t value, + uint32_t mask, void *next, bool skip_b, bool skip_r, int id, + uint32_t module) +{ + assert(plink != NULL); + + FILL_PLINK_HEAD(plink, 0, REGDMA_LINK_MODE_WAIT, 0, skip_r, skip_b, !next); + plink->body.next = next; + plink->body.backup = backup; + plink->body.value = value; + plink->body.mask = mask; + FILL_PLINK_STAT(plink, 0, (uint16_t)id, module); + + return (void *)plink; +} + +static inline void * regdma_link_init_branch_continuous( + regdma_link_branch_continuous_t *plink, void *buff, void *backup, int len, void *restore, + regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + assert(plink != NULL); + assert(buff !=NULL); + + FILL_PLINK_HEAD(plink, len, REGDMA_LINK_MODE_CONTINUOUS, 1, skip_r, skip_b, 0); + plink->body.backup = backup; + plink->body.restore = restore; + plink->body.mem = buff; + for (int i = 0; i < REGDMA_LINK_ENTRY_NUM; i++) { + plink->body.next[i] = (*next)[i]; + } + FILL_PLINK_STAT(plink, 0, (uint16_t)id, module); + + return (void *)plink; +} + +static inline void * regdma_link_init_branch_addr_map( + regdma_link_branch_addr_map_t *plink, void *buff, void *backup, uint32_t bitmap[4], + int len, void *restore, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, + uint32_t module) +{ + assert(plink != NULL); + assert(buff != NULL); + + FILL_PLINK_HEAD(plink, len, REGDMA_LINK_MODE_ADDR_MAP, 1, skip_r, skip_b, 0); + plink->body.backup = backup; + plink->body.restore = restore; + plink->body.mem = buff; + memcpy(plink->body.next, *next, REGDMA_LINK_ENTRY_NUM * sizeof((*next)[0])); + plink->body.map[0] = bitmap[0]; + plink->body.map[1] = bitmap[1]; + plink->body.map[2] = bitmap[2]; + plink->body.map[3] = bitmap[3]; + FILL_PLINK_STAT(plink, 0, (uint16_t)id, module); + + return (void *)plink; +} + +static inline void * regdma_link_init_branch_write( + regdma_link_branch_write_wait_t *plink, void *backup, uint32_t value, uint32_t mask, + regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + assert(plink != NULL); + + FILL_PLINK_HEAD(plink, 0, REGDMA_LINK_MODE_WRITE, 1, skip_r, skip_b, 0); + plink->body.backup = backup; + plink->body.value = value; + plink->body.mask = mask; + for (int i = 0; i < REGDMA_LINK_ENTRY_NUM; i++) { + plink->body.next[i] = (*next)[i]; + } + FILL_PLINK_STAT(plink, 0, (uint16_t)id, module); + + return (void *)plink; +} + +static inline void * regdma_link_init_branch_wait( + regdma_link_branch_write_wait_t *plink, void *backup, uint32_t value, uint32_t mask, + regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + assert(plink != NULL); + + FILL_PLINK_HEAD(plink, 0, REGDMA_LINK_MODE_WAIT, 1, skip_r, skip_b, 0); + plink->body.backup = backup; + plink->body.value = value; + plink->body.mask = mask; + for (int i = 0; i < REGDMA_LINK_ENTRY_NUM; i++) { + plink->body.next[i] = (*next)[i]; + } + FILL_PLINK_STAT(plink, 0, (uint16_t)id, module); + + return (void *)plink; +} + +static inline void regdma_link_update_stats(regdma_link_stats_t *stats, int entry, int depth) +{ + assert(stats != NULL); + + stats->ref |= BIT(entry); +} + +#endif // SOC_PAU_SUPPORTED + +#endif /* __REGDMA_LINK_H__ */ diff --git a/components/esp_hw_support/port/regdma_link.c b/components/esp_hw_support/port/regdma_link.c new file mode 100644 index 0000000000..d941b87540 --- /dev/null +++ b/components/esp_hw_support/port/regdma_link.c @@ -0,0 +1,798 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include + +#include "esp_private/regdma_link.h" + +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_regdma.h" + + + +#define REGDMA_LINK_ADDR_ALIGN (4) +#define REGDMA_LINK_MEM_TYPE_CAPS (MALLOC_CAP_DMA | MALLOC_CAP_DEFAULT) + +void * regdma_link_new_continuous(void *backup, void *buff, int len, void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + regdma_link_continuous_t *link = (regdma_link_continuous_t *)heap_caps_aligned_alloc( + REGDMA_LINK_ADDR_ALIGN, + buff ? sizeof(regdma_link_continuous_t) : (sizeof(regdma_link_continuous_t) + (len<<2)), + REGDMA_LINK_MEM_TYPE_CAPS + ); + if (link) { + memset(link, 0, buff ? sizeof(regdma_link_continuous_t) : (sizeof(regdma_link_continuous_t) + (len<<2))); + void *buf = buff ? buff : (void *)(link->buff); + link = regdma_link_init_continuous(link, buf, backup, len, restore, next, skip_b, skip_r, id, module); + return (void *)((void *)link + offsetof(regdma_link_continuous_t, head)); + } + return NULL; +} + +void * regdma_link_new_addr_map(void *backup, void *buff, uint32_t bitmap[4], int len, void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + regdma_link_addr_map_t *link = (regdma_link_addr_map_t *)heap_caps_aligned_alloc( + REGDMA_LINK_ADDR_ALIGN, + buff ? sizeof(regdma_link_addr_map_t) : (sizeof(regdma_link_addr_map_t) + (len<<2)), + REGDMA_LINK_MEM_TYPE_CAPS + ); + if (link) { + memset(link, 0, buff ? sizeof(regdma_link_addr_map_t) : (sizeof(regdma_link_addr_map_t) + (len<<2))); + void *buf = buff ? buff : (void *)(link->buff); + link = regdma_link_init_addr_map(link, buf, backup, bitmap, len, restore, next, skip_b, skip_r, id, module); + return (void *)((void *)link + offsetof(regdma_link_addr_map_t, head)); + } + return NULL; +} + +void * regdma_link_new_write(void *backup, uint32_t value, uint32_t mask, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + regdma_link_write_wait_t *link = (regdma_link_write_wait_t *)heap_caps_aligned_alloc( + REGDMA_LINK_ADDR_ALIGN, sizeof(regdma_link_write_wait_t), REGDMA_LINK_MEM_TYPE_CAPS); + if (link) { + memset(link, 0, sizeof(regdma_link_write_wait_t)); + link = regdma_link_init_write(link, backup, value, mask, next, skip_b, skip_r, id, module); + return (void *)((void *)link + offsetof(regdma_link_write_wait_t, head)); + } + return NULL; +} + +void * regdma_link_new_wait(void *backup, uint32_t value, uint32_t mask, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + regdma_link_write_wait_t *link = (regdma_link_write_wait_t *)heap_caps_aligned_alloc( + REGDMA_LINK_ADDR_ALIGN, sizeof(regdma_link_write_wait_t), REGDMA_LINK_MEM_TYPE_CAPS); + if (link) { + memset(link, 0, sizeof(regdma_link_write_wait_t)); + link = regdma_link_init_wait(link, backup, value, mask, next, skip_b, skip_r, id, module); + return (void *)((void *)link + offsetof(regdma_link_write_wait_t, head)); + } + return NULL; +} + +void * regdma_link_new_branch_continuous(void *backup, void *buff, int len, void *restore, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + regdma_link_branch_continuous_t *link = (regdma_link_branch_continuous_t *)heap_caps_aligned_alloc( + REGDMA_LINK_ADDR_ALIGN, + buff ? sizeof(regdma_link_branch_continuous_t) : (sizeof(regdma_link_branch_continuous_t) + (len<<2)), + REGDMA_LINK_MEM_TYPE_CAPS + ); + if (link) { + memset(link, 0, buff ? sizeof(regdma_link_branch_continuous_t) : (sizeof(regdma_link_branch_continuous_t) + (len<<2))); + void *buf = buff ? buff : (void *)(link->buff); + link = regdma_link_init_branch_continuous(link, buf, backup, len, restore, next, skip_b, skip_r, id, module); + return (void *)((void *)link + offsetof(regdma_link_branch_continuous_t, head)); + } + return NULL; +} + +void * regdma_link_new_branch_addr_map(void *backup, void *buff, uint32_t bitmap[4], int len, void *restore, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + regdma_link_branch_addr_map_t *link = (regdma_link_branch_addr_map_t *)heap_caps_aligned_alloc( + REGDMA_LINK_ADDR_ALIGN, + buff ? sizeof(regdma_link_branch_addr_map_t) : (sizeof(regdma_link_branch_addr_map_t) + (len<<2)), + REGDMA_LINK_MEM_TYPE_CAPS + ); + if (link) { + memset(link, 0, buff ? sizeof(regdma_link_branch_addr_map_t) : (sizeof(regdma_link_branch_addr_map_t) + (len<<2))); + void *buf = buff ? buff : (void *)(link->buff); + link = regdma_link_init_branch_addr_map(link, buf, backup, bitmap, len, restore, next, skip_b, skip_r, id, module); + return (void *)((void *)link + offsetof(regdma_link_branch_addr_map_t, head)); + } + return NULL; +} + +void * regdma_link_new_branch_write(void *backup, uint32_t value, uint32_t mask, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + regdma_link_branch_write_wait_t *link = (regdma_link_branch_write_wait_t *)heap_caps_aligned_alloc( + REGDMA_LINK_ADDR_ALIGN, sizeof(regdma_link_branch_write_wait_t), REGDMA_LINK_MEM_TYPE_CAPS); + if (link) { + memset(link, 0, sizeof(regdma_link_branch_write_wait_t)); + link = regdma_link_init_branch_write(link, backup, value, mask, next, skip_b, skip_r, id, module); + return (void *)((void *)link + offsetof(regdma_link_branch_write_wait_t, head)); + } + return NULL; +} + +void * regdma_link_new_branch_wait(void *backup, uint32_t value, uint32_t mask, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + regdma_link_branch_write_wait_t *link = (regdma_link_branch_write_wait_t *)heap_caps_aligned_alloc( + REGDMA_LINK_ADDR_ALIGN, sizeof(regdma_link_branch_write_wait_t), REGDMA_LINK_MEM_TYPE_CAPS); + if (link) { + memset(link, 0, sizeof(regdma_link_branch_write_wait_t)); + link = regdma_link_init_branch_wait(link, backup, value, mask, next, skip_b, skip_r, id, module); + return (void *)((void *)link + offsetof(regdma_link_branch_write_wait_t, head)); + } + return NULL; +} + +void * regdma_link_new_continuous_default(void *backup, int len, void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + return regdma_link_new_continuous(backup, NULL, len, restore, next, skip_b, skip_r, id, module); +} + +void * regdma_link_new_addr_map_default(void *backup, uint32_t bitmap[4], int len, void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + return regdma_link_new_addr_map(backup, NULL, bitmap, len, restore, next, skip_b, skip_r, id, module); +} + +void * regdma_link_new_write_default(void *backup, uint32_t value, uint32_t mask, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + return regdma_link_new_write(backup, value, mask, next, skip_b, skip_r, id, module); +} + +void * regdma_link_new_wait_default(void *backup, uint32_t value, uint32_t mask, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + return regdma_link_new_wait(backup, value, mask, next, skip_b, skip_r, id, module); +} + +void * regdma_link_new_branch_continuous_default(void *backup, int len, void *restore, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + return regdma_link_new_branch_continuous(backup, NULL, len, restore, next, skip_b, skip_r, id, module); +} + +void * regdma_link_new_branch_addr_map_default(void *backup, uint32_t bitmap[4], int len, void *restore, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + return regdma_link_new_branch_addr_map(backup, NULL, bitmap, len, restore, next, skip_b, skip_r, id, module); +} + +void * regdma_link_new_branch_write_default(void *backup, uint32_t value, uint32_t mask, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + return regdma_link_new_branch_write(backup, value, mask, next, skip_b, skip_r, id, module); +} + +void * regdma_link_new_branch_wait_default(void *backup, uint32_t value, uint32_t mask, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + return regdma_link_new_branch_wait(backup, value, mask, next, skip_b, skip_r, id, module); +} + + +static void * regdma_link_init_continuous_wrapper(const regdma_link_config_t *config, uint32_t module, int n, va_list args) +{ + regdma_entry_buf_t next; + + memset(next, 0, sizeof(regdma_entry_buf_t)); + for (int i = 0; i < n && i < REGDMA_LINK_ENTRY_NUM; i++) { // Ignore more arguments + next[i] = va_arg(args, void *); + } + return regdma_link_new_continuous_default((void *)(config->continuous.backup), config->head.length, + (void *)(config->continuous.restore), next[0], config->head.skip_b, + config->head.skip_r, config->id, module); +} + +static void * regdma_link_init_addr_map_wrapper(const regdma_link_config_t *config, uint32_t module, int n, va_list args) +{ + regdma_entry_buf_t next; + + memset(next, 0, sizeof(regdma_entry_buf_t)); + for (int i = 0; i < n && i < REGDMA_LINK_ENTRY_NUM; i++) { // Ignore more arguments + next[i] = va_arg(args, void *); + } + return regdma_link_new_addr_map_default((void *)(config->addr_map.backup), (void *)(config->addr_map.map), + config->head.length, (void *)(config->addr_map.restore), next[0], config->head.skip_b, + config->head.skip_r, config->id, module); +} + +static void * regdma_link_init_write_wrapper(const regdma_link_config_t *config, uint32_t module, int n, va_list args) +{ + regdma_entry_buf_t next; + + memset(next, 0, sizeof(regdma_entry_buf_t)); + for (int i = 0; i < n && i < REGDMA_LINK_ENTRY_NUM; i++) { // Ignore more arguments + next[i] = va_arg(args, void *); + } + return regdma_link_new_write_default((void *)(config->write_wait.backup), config->write_wait.value, + config->write_wait.mask, next[0], config->head.skip_b, config->head.skip_r, + config->id, module); +} + +static void * regdma_link_init_wait_wrapper(const regdma_link_config_t *config, uint32_t module, int n, va_list args) +{ + regdma_entry_buf_t next; + + memset(next, 0, sizeof(regdma_entry_buf_t)); + for (int i = 0; i < n && i < REGDMA_LINK_ENTRY_NUM; i++) { // Ignore more arguments + next[i] = va_arg(args, void *); + } + return regdma_link_new_wait_default((void *)(config->write_wait.backup), config->write_wait.value, + config->write_wait.mask, next[0], config->head.skip_b, config->head.skip_r, + config->id, module); +} + +static void * regdma_link_init_branch_continuous_wrapper(const regdma_link_config_t *config, uint32_t module, int n, va_list args) +{ + regdma_entry_buf_t next; + + memset(next, 0, sizeof(regdma_entry_buf_t)); + for (int i = 0; i < n && i < REGDMA_LINK_ENTRY_NUM; i++) { // Ignore more arguments + next[i] = va_arg(args, void *); + } + return regdma_link_new_branch_continuous_default((void *)(config->continuous.backup), + config->head.length, (void *)(config->continuous.restore), &next, + config->head.skip_b, config->head.skip_r, config->id, module); +} + +static void * regdma_link_init_branch_addr_map_wrapper(const regdma_link_config_t *config, uint32_t module, int n, va_list args) +{ + regdma_entry_buf_t next; + + memset(next, 0, sizeof(regdma_entry_buf_t)); + for (int i = 0; i < n && i < REGDMA_LINK_ENTRY_NUM; i++) { // Ignore more arguments + next[i] = va_arg(args, void *); + } + return regdma_link_new_branch_addr_map_default((void *)(config->addr_map.backup), + (void *)(config->addr_map.map), config->head.length, (void *)(config->addr_map.restore), + &next, config->head.skip_b, config->head.skip_r, config->id, module); +} + +static void * regdma_link_init_branch_write_wrapper(const regdma_link_config_t *config, uint32_t module, int n, va_list args) +{ + regdma_entry_buf_t next; + + memset(next, 0, sizeof(regdma_entry_buf_t)); + for (int i = 0; i < n && i < REGDMA_LINK_ENTRY_NUM; i++) { // Ignore more arguments + next[i] = va_arg(args, void *); + } + return regdma_link_new_branch_write_default((void *)(config->write_wait.backup), + config->write_wait.value, config->write_wait.mask, &next, config->head.skip_b, + config->head.skip_r, config->id, module); +} + +static void * regdma_link_init_branch_wait_wrapper(const regdma_link_config_t *config, uint32_t module, int n, va_list args) +{ + regdma_entry_buf_t next; + + memset(next, 0, sizeof(regdma_entry_buf_t)); + for (int i = 0; i < n && i < REGDMA_LINK_ENTRY_NUM; i++) { // Ignore more arguments + next[i] = va_arg(args, void *); + } + return regdma_link_new_branch_wait_default((void *)(config->write_wait.backup), + config->write_wait.value, config->write_wait.mask, &next, config->head.skip_b, + config->head.skip_r, config->id, module); +} + +static void * regdma_link_init_wrapper(const regdma_link_config_t *config, bool branch, uint32_t module, int nentry, va_list args) +{ + typedef void * (*init_fn_t)(const void *, uint32_t, int, va_list); + + const static init_fn_t initfn[] = { + [0] = (init_fn_t)regdma_link_init_continuous_wrapper, /* REGDMA_LINK_MODE_CONTINUOUS */ + [1] = (init_fn_t)regdma_link_init_addr_map_wrapper, /* REGDMA_LINK_MODE_ADDR_MAP */ + [2] = (init_fn_t)regdma_link_init_write_wrapper, /* REGDMA_LINK_MODE_WRITE */ + [3] = (init_fn_t)regdma_link_init_wait_wrapper /* REGDMA_LINK_MODE_WAIT */ + }; + const static init_fn_t initfn_b[] = { + [0] = (init_fn_t)regdma_link_init_branch_continuous_wrapper, + [1] = (init_fn_t)regdma_link_init_branch_addr_map_wrapper, + [2] = (init_fn_t)regdma_link_init_branch_write_wrapper, + [3] = (init_fn_t)regdma_link_init_branch_wait_wrapper + }; + + assert((config->head.mode < ARRAY_SIZE(initfn)) && (config->head.mode < ARRAY_SIZE(initfn_b))); + + init_fn_t pfn = branch ? initfn_b[config->head.mode] : initfn[config->head.mode]; + return (*pfn)(config, module, nentry, args); +} + +void * regdma_link_init(const regdma_link_config_t *config, bool branch, uint32_t module, int nentry, ...) +{ + assert(config != NULL); + + va_list args; + va_start(args, nentry); + void * link = regdma_link_init_wrapper(config, branch, module, nentry, args); + va_end(args); + return link; +} + +static void * regdma_link_get_next_continuous_wrapper(void *link) +{ + regdma_link_continuous_t *continuous = __containerof(link, regdma_link_continuous_t, head); + return (void *)(continuous->body.next); +} + +static void * regdma_link_get_next_addr_map_wrapper(void *link) +{ + regdma_link_addr_map_t *addr_map = __containerof(link, regdma_link_addr_map_t, head); + return (void *)(addr_map->body.next); +} + +static void * regdma_link_get_next_write_wait_wrapper(void *link) +{ + regdma_link_write_wait_t *write_wait = __containerof(link, regdma_link_write_wait_t, head); + return (void *)(write_wait->body.next); +} + +static regdma_entry_buf_t * regdma_link_get_next_branch_continuous_wrapper(void *link) +{ + regdma_link_branch_continuous_t *branch_continuous = __containerof(link, regdma_link_branch_continuous_t, head); + return &branch_continuous->body.next; +} + +static regdma_entry_buf_t * regdma_link_get_next_branch_addr_map_wrapper(void *link) +{ + regdma_link_branch_addr_map_t *branch_addr_map = __containerof(link, regdma_link_branch_addr_map_t, head); + return &branch_addr_map->body.next; +} + +static regdma_entry_buf_t * regdma_link_get_next_branch_write_wait_wrapper(void *link) +{ + regdma_link_branch_write_wait_t *branch_write_wait = __containerof(link, regdma_link_branch_write_wait_t, head); + return &branch_write_wait->body.next; +} + +static void * regdma_link_get_next(void *link, int entry) +{ + if (link) { + regdma_link_head_t head = REGDMA_LINK_HEAD(link); + if (head.branch) { + typedef regdma_entry_buf_t * (*get_nextfn1_t)(void *); + const static get_nextfn1_t nextfn1[] = { + [0] = (get_nextfn1_t)regdma_link_get_next_branch_continuous_wrapper, + [1] = (get_nextfn1_t)regdma_link_get_next_branch_addr_map_wrapper, + [2] = (get_nextfn1_t)regdma_link_get_next_branch_write_wait_wrapper, + [3] = (get_nextfn1_t)regdma_link_get_next_branch_write_wait_wrapper + }; + assert(head.mode < ARRAY_SIZE(nextfn1)); + regdma_entry_buf_t *next = (*nextfn1[head.mode])(link); + if ((entry < REGDMA_LINK_ENTRY_NUM) && (*next)[entry] && (head.eof == 0)) { + return (*next)[entry]; + } + } else { + typedef void * (*get_nextfn0_t)(void *); + const static get_nextfn0_t nextfn0[] = { + [0] = (get_nextfn0_t)regdma_link_get_next_continuous_wrapper, + [1] = (get_nextfn0_t)regdma_link_get_next_addr_map_wrapper, + [2] = (get_nextfn0_t)regdma_link_get_next_write_wait_wrapper, + [3] = (get_nextfn0_t)regdma_link_get_next_write_wait_wrapper + }; + assert(head.mode < ARRAY_SIZE(nextfn0)); + void *next = (*nextfn0[head.mode])(link); + if (next && (head.eof == 0)) { + return next; + } + } + } + return NULL; +} + +static void * regdma_link_recursive_impl(void *link, int entry, int depth, void (*hook)(void *, int, int)) +{ + assert(entry < REGDMA_LINK_ENTRY_NUM); + + if (link) { + regdma_link_recursive_impl(regdma_link_get_next(link, entry), entry, depth+1, hook); + if (hook) { + (*hook)(link, entry, depth); + } + } + return link; +} + +void * regdma_link_recursive(void *link, int entry, void (*hook)(void *, int, int)) +{ + return regdma_link_recursive_impl(link, entry, 0, hook); +} + +static void * regdma_link_get_instance(void *link) +{ + void * container_memaddr[] = { + (void *)__containerof(link, regdma_link_continuous_t, head), + (void *)__containerof(link, regdma_link_addr_map_t, head), + (void *)__containerof(link, regdma_link_write_wait_t, head), + (void *)__containerof(link, regdma_link_write_wait_t, head), + (void *)__containerof(link, regdma_link_branch_continuous_t, head), + (void *)__containerof(link, regdma_link_branch_addr_map_t, head), + (void *)__containerof(link, regdma_link_branch_write_wait_t, head), + (void *)__containerof(link, regdma_link_branch_write_wait_t, head) + }; + regdma_link_head_t head = REGDMA_LINK_HEAD(link); + int it = (head.branch << 2) | head.mode; + assert(it < ARRAY_SIZE(container_memaddr)); + + return container_memaddr[it]; +} +static regdma_link_stats_t * regdma_link_get_stats(void *link) +{ + const static size_t stats_offset[] = { + offsetof(regdma_link_continuous_t, stat), + offsetof(regdma_link_addr_map_t, stat), + offsetof(regdma_link_write_wait_t, stat), + offsetof(regdma_link_write_wait_t, stat), + offsetof(regdma_link_branch_continuous_t, stat), + offsetof(regdma_link_branch_addr_map_t, stat), + offsetof(regdma_link_branch_write_wait_t, stat), + offsetof(regdma_link_branch_write_wait_t, stat) + }; + regdma_link_head_t head = REGDMA_LINK_HEAD(link); + int it = (head.branch << 2) | head.mode; + assert(it < ARRAY_SIZE(stats_offset)); + + return (regdma_link_stats_t *)(regdma_link_get_instance(link) + stats_offset[it]); +} + +static void regdma_link_update_stats_wrapper(void *link, int entry, int depth) +{ + if (link == NULL) { + return; + } + regdma_link_update_stats(regdma_link_get_stats(link), entry, depth); +} + +void regdma_link_stats(void *link, int entry) +{ + regdma_link_recursive_impl(link, entry, 0, regdma_link_update_stats_wrapper); +} + +static void regdma_link_destroy_wrapper(void *link, int entry, int depth) +{ + if (link == NULL) { + return; + } + regdma_link_stats_t *stat = regdma_link_get_stats(link); + stat->ref &= ~BIT(entry); + if (stat->ref == 0) { + free(regdma_link_get_instance(link)); + } +} + +void regdma_link_destroy(void *link, int entry) +{ + regdma_link_recursive_impl(link, entry, 0, regdma_link_destroy_wrapper); +} + +void * regdma_find_link_by_pos(void *link, int entry, int pos) +{ + assert(entry < REGDMA_LINK_ENTRY_NUM); + + void *next = link; + if (link) { + int iter = 0; + do { + if (pos == iter++) { + break; + } + } while ((next = regdma_link_get_next(next, entry)) != NULL); + } + return next; +} + +void * regdma_find_link_by_id(void *link, int entry, int id) +{ + assert(entry < REGDMA_LINK_ENTRY_NUM); + + void *find_addr = NULL; + void *next = link; + if (link) { + int linkid = 0; + do { + regdma_link_head_t head = REGDMA_LINK_HEAD(next); + if (head.branch) { + regdma_link_branch_continuous_t *continuous = (regdma_link_branch_continuous_t *)regdma_link_get_instance(next); + linkid = continuous->stat.id; + } else { + regdma_link_continuous_t *continuous = (regdma_link_continuous_t *)regdma_link_get_instance(next); + linkid = continuous->stat.id; + } + if (linkid == id) { + find_addr = next; + break; + } + } while ((next = regdma_link_get_next(next, entry)) != NULL); + } + return find_addr; +} + +void regdma_link_set_write_wait_content(void *link, uint32_t value, uint32_t mask) +{ + if (link) { + regdma_link_head_t head = REGDMA_LINK_HEAD(link); + if (head.mode == REGDMA_LINK_MODE_WRITE || head.mode == REGDMA_LINK_MODE_WAIT) { + if (head.branch) { + regdma_link_branch_write_wait_t *write_wait = (regdma_link_branch_write_wait_t *)regdma_link_get_instance(link); + write_wait->body.value = value; + write_wait->body.mask = mask; + } else { + regdma_link_write_wait_t *write_wait = (regdma_link_write_wait_t *)regdma_link_get_instance(link); + write_wait->body.value = value; + write_wait->body.mask = mask; + } + } + } +} + +static void regdma_link_update_continuous_next_wrapper(void *link, void *next) +{ + regdma_link_continuous_t *continuous = __containerof(link, regdma_link_continuous_t, head); + continuous->body.next = next; +} + +static void regdma_link_update_addr_map_next_wrapper(void *link, void *next) +{ + regdma_link_addr_map_t *addr_map = __containerof(link, regdma_link_addr_map_t, head); + addr_map->body.next = next; +} + +static void regdma_link_update_write_wait_next_wrapper(void *link, void *next) +{ + regdma_link_write_wait_t *write_wait = __containerof(link, regdma_link_write_wait_t, head); + write_wait->body.next = next; +} + +static void regdma_link_update_branch_continuous_next_wrapper(void *link, regdma_entry_buf_t *next) +{ + regdma_link_branch_continuous_t *branch_continuous = __containerof(link, regdma_link_branch_continuous_t, head); + for (int i = 0; i < REGDMA_LINK_ENTRY_NUM; i++) { + branch_continuous->body.next[i] = (*next)[i]; + } +} + +static void regdma_link_update_branch_addr_map_next_wrapper(void *link, regdma_entry_buf_t *next) +{ + regdma_link_branch_addr_map_t *branch_addr_map = __containerof(link, regdma_link_branch_addr_map_t, head); + for (int i = 0; i < REGDMA_LINK_ENTRY_NUM; i++) { + branch_addr_map->body.next[i] = (*next)[i]; + } +} + +static void regdma_link_update_branch_write_wait_next_wrapper(void *link, regdma_entry_buf_t *next) +{ + regdma_link_branch_write_wait_t *branch_write_wait = __containerof(link, regdma_link_branch_write_wait_t, head); + for (int i = 0; i < REGDMA_LINK_ENTRY_NUM; i++) { + branch_write_wait->body.next[i] = (*next)[i]; + } +} + +void regdma_link_update_next(void *link, int nentry, ...) +{ + va_list args; + va_start(args, nentry); + if (link) { + regdma_entry_buf_t next; + memset(next, 0, sizeof(regdma_entry_buf_t)); + for (int i = 0; i < nentry && i < REGDMA_LINK_ENTRY_NUM; i++) { // Ignore more arguments + next[i] = va_arg(args, void *); + } + + regdma_link_head_t head = REGDMA_LINK_HEAD(link); + if (head.branch) { + typedef void (*update_branch_fn_t)(void *, regdma_entry_buf_t *); + static const update_branch_fn_t updatefn_b[] = { + [0] = regdma_link_update_branch_continuous_next_wrapper, + [1] = regdma_link_update_branch_addr_map_next_wrapper, + [2] = regdma_link_update_branch_write_wait_next_wrapper, + [3] = regdma_link_update_branch_write_wait_next_wrapper + }; + assert((head.mode < ARRAY_SIZE(updatefn_b))); + (*updatefn_b[head.mode])(link, &next); + } else { + typedef void (*update_fn_t)(void *, void *); + static const update_fn_t updatefn[] = { + [0] = regdma_link_update_continuous_next_wrapper, + [1] = regdma_link_update_addr_map_next_wrapper, + [2] = regdma_link_update_write_wait_next_wrapper, + [3] = regdma_link_update_write_wait_next_wrapper + }; + assert((head.mode < ARRAY_SIZE(updatefn))); + (*updatefn[head.mode])(link, next[0]); + } + } + va_end(args); +} + +uint32_t regdma_link_get_owner_bitmap(void *link, void *tail, int entry) +{ + assert(entry < REGDMA_LINK_ENTRY_NUM); + + uint32_t owner = 0; + void *next = link; + if (link) { + do { + owner |= regdma_link_get_stats(next)->ref; + if (next == tail) { + break; + } + } while ((next = regdma_link_get_next(next, entry)) != NULL); + } + return owner; +} + +void * regdma_find_module_link_head(void *link, void *tail, int entry, uint32_t module) +{ + assert(entry < REGDMA_LINK_ENTRY_NUM); + + void *find_link = NULL; + void *next = link; + if (link) { + do { + if (next == tail) { + break; + } + if (regdma_link_get_stats(next)->module & module) { + find_link = next; + break; + } + } while ((next = regdma_link_get_next(next, entry)) != NULL); + } + return find_link; +} + +void * regdma_find_module_link_tail(void *link, void *tail, int entry, uint32_t module) +{ + assert(entry < REGDMA_LINK_ENTRY_NUM); + + void *find_tail = NULL; + void *next = link; + if (link) { + do { + if (next != tail) { + void *temp = regdma_link_get_next(next, entry); + if ((regdma_link_get_stats(next)->module & module) && + !(regdma_link_get_stats(temp)->module & module)) { + find_tail = next; + break; + } + } else { + if (regdma_link_get_stats(next)->module & module) { + find_tail = next; + break; + } + } + } while ((next = regdma_link_get_next(next, entry)) != NULL); + } + return find_tail; +} + +void * regdma_find_next_module_link_head(void *link, void *tail, int entry, uint32_t module) +{ + assert(entry < REGDMA_LINK_ENTRY_NUM); + void *find_tail = regdma_find_module_link_tail(link, tail, entry, module); + if (find_tail && find_tail != tail) { + return regdma_link_get_next(find_tail, entry); + } + return NULL; +} + +void * regdma_find_prev_module_link_tail(void *link, void *tail, int entry, uint32_t module) +{ + assert(entry < REGDMA_LINK_ENTRY_NUM); + void *find_head = regdma_find_module_link_head(link, tail, entry, module); + void *next = link; + if (find_head && find_head != link) { + do { + if (next == tail) { + break; + } + if (regdma_link_get_next(next, entry) == find_head) { + return next; + } + } while ((next = regdma_link_get_next(next, entry)) != NULL); + } + return NULL; +} + +#if REGDMA_LINK_DBG +static const char *TAG = "regdma_link"; + +static void print_info_continuous_wrapper(void *link) +{ + regdma_link_head_t head = REGDMA_LINK_HEAD(link); + regdma_link_continuous_t *cons = __containerof(link, regdma_link_continuous_t, head); + ESP_EARLY_LOGI(TAG, "[%08x/%04x] link:%x, head:%x, next:%x, backup:%x, restore:%x, buff:%x", + cons->stat.module, cons->stat.id, link, cons->head, cons->body.next, + cons->body.backup, cons->body.restore, cons->body.mem); + ESP_LOG_BUFFER_HEX(TAG, (const void *)cons->body.mem, head.length); +} + +static void print_info_addr_map_wrapper(void *link) +{ + regdma_link_head_t head = REGDMA_LINK_HEAD(link); + regdma_link_addr_map_t *map = __containerof(link, regdma_link_addr_map_t, head); + ESP_EARLY_LOGI(TAG, "[%08x/%04x] link:%x, head:%x, next:%x, backup:%x, restore:%x, buff:%x, map:{%x,%x,%x,%x}", + map->stat.module, map->stat.id, link, map->head, map->body.next, map->body.backup, + map->body.restore, map->body.mem, map->body.map[0], map->body.map[1], + map->body.map[2], map->body.map[3]); + ESP_LOG_BUFFER_HEX(TAG, (const void *)map->body.mem, head.length); +} + +static void print_info_write_wait_wrapper(void *link) +{ + regdma_link_write_wait_t *ww = __containerof(link, regdma_link_write_wait_t, head); + ESP_EARLY_LOGI(TAG, "[%08x/%04x] link:%x, head:%x, next:%x, backup:%x, value:%x, mask:%x", + ww->stat.module, ww->stat.id, link, ww->head, ww->body.next, + ww->body.backup, ww->body.value, ww->body.mask); +} + +static void print_info_branch_continuous_wrapper(void *link) +{ + regdma_link_head_t head = REGDMA_LINK_HEAD(link); + regdma_link_branch_continuous_t *cons = __containerof(link, regdma_link_branch_continuous_t, head); + ESP_EARLY_LOGI(TAG, "[%08x/%04x] link:%x, head:%x, next:{%x,%x,%x,%x}, backup:%x, restore:%x, buff:%x", + cons->stat.module, cons->stat.id, link, cons->head, cons->body.next[0], cons->body.next[1], + cons->body.next[2], cons->body.next[3], cons->body.backup, cons->body.restore, + cons->body.mem); + ESP_LOG_BUFFER_HEX(TAG, (const void *)cons->body.mem, head.length); +} + +static void print_info_branch_addr_map_wrapper(void *link) +{ + regdma_link_head_t head = REGDMA_LINK_HEAD(link); + regdma_link_branch_addr_map_t *map = __containerof(link, regdma_link_branch_addr_map_t, head); + ESP_EARLY_LOGI(TAG, "[%08x/%04x] link:%x, head:%x, next:{%x,%x,%x,%x}, backup:%x, restore:%x, buff:%x, map:{%x,%x,%x,%x}", + map->stat.module, map->stat.id, link, map->head, map->body.next[0], map->body.next[1], map->body.next[2], + map->body.next[3], map->body.backup, map->body.restore, map->body.mem, map->body.map[0], + map->body.map[1], map->body.map[2], map->body.map[3]); + ESP_LOG_BUFFER_HEX(TAG, (const void *)map->body.mem, head.length); +} + +static void print_info_branch_write_wait_wrapper(void *link) +{ + regdma_link_branch_write_wait_t *ww = __containerof(link, regdma_link_branch_write_wait_t, head); + ESP_EARLY_LOGI(TAG, "[%08x/%04x] link:%x, head:%x, next:{%x,%x,%x,%x}, backup:%x, value:%x, mask:%x", + ww->stat.module, ww->stat.id, link, ww->head, ww->body.next[0], ww->body.next[1], + ww->body.next[2], ww->body.next[3], ww->body.backup, ww->body.value, + ww->body.mask); +} + +static void print_link_info(void *args, int entry, int depth) +{ + typedef void (*prinf_fn_t)(void *); + + const static prinf_fn_t prinf_fn[] = { + [0] = (prinf_fn_t)print_info_continuous_wrapper, + [1] = (prinf_fn_t)print_info_addr_map_wrapper, + [2] = (prinf_fn_t)print_info_write_wait_wrapper, + [3] = (prinf_fn_t)print_info_write_wait_wrapper, + [4] = (prinf_fn_t)print_info_branch_continuous_wrapper, + [5] = (prinf_fn_t)print_info_branch_addr_map_wrapper, + [6] = (prinf_fn_t)print_info_branch_write_wait_wrapper, + [7] = (prinf_fn_t)print_info_branch_write_wait_wrapper + }; + + regdma_link_head_t head = REGDMA_LINK_HEAD(args); + int it = (head.branch << 2) | head.mode; + assert(it < ARRAY_SIZE(prinf_fn)); + + (*prinf_fn[it])(args); +} + +void regdma_link_show_memories(void *link, int entry) +{ + assert(entry < REGDMA_LINK_ENTRY_NUM); + + void *next = link; + if (link) { + do { + print_link_info(next, entry, 0); + } while ((next = regdma_link_get_next(next, entry)) != NULL); + } else { + ESP_EARLY_LOGW(TAG, "This REGDMA linked list is empty!\n"); + } +} +#endif