async_memcpy_gdma.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. /*
  2. * SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <string.h>
  7. #include <stdatomic.h>
  8. #include <sys/queue.h>
  9. #include <sys/param.h>
  10. #include "sdkconfig.h"
  11. #include "freertos/FreeRTOS.h"
  12. #include "soc/soc_caps.h"
  13. #include "esp_log.h"
  14. #include "esp_check.h"
  15. #include "esp_attr.h"
  16. #include "esp_err.h"
  17. #include "esp_private/gdma.h"
  18. #include "esp_memory_utils.h"
  19. #include "esp_async_memcpy.h"
  20. #include "esp_async_memcpy_priv.h"
  21. #include "hal/dma_types.h"
  22. #include "hal/cache_hal.h"
  23. #include "hal/cache_ll.h"
  24. #include "rom/cache.h"
  25. static const char *TAG = "async_mcp.gdma";
  26. #define MCP_NEEDS_INVALIDATE_DST_CACHE CONFIG_IDF_TARGET_ESP32P4
  27. #define MCP_NEEDS_WRITE_BACK_SRC_CACHE CONFIG_IDF_TARGET_ESP32P4
  28. #define MCP_NEEDS_WRITE_BACK_DESC_CACHE CONFIG_IDF_TARGET_ESP32P4
  29. #if SOC_AXI_GDMA_SUPPORTED
  30. #define MCP_DMA_DESC_ALIGN 64
  31. typedef dma_descriptor_align8_t mcp_dma_descriptor_t;
  32. #elif SOC_AHB_GDMA_SUPPORTED
  33. #define MCP_DMA_DESC_ALIGN 32
  34. typedef dma_descriptor_align4_t mcp_dma_descriptor_t;
  35. #else
  36. #error "Unsupported GDMA type"
  37. #endif
  38. /// @brief Transaction object for async memcpy
  39. /// @note - GDMA requires the DMA descriptors to be 4 or 8 bytes aligned
  40. /// @note - The DMA descriptor link list is allocated dynamically from DMA-able memory
  41. /// @note - Because of the eof_node, the transaction object should also be allocated from DMA-able memory
  42. typedef struct async_memcpy_transaction_t {
  43. mcp_dma_descriptor_t eof_node; // this is the DMA node which act as the EOF descriptor (RX path only)
  44. mcp_dma_descriptor_t *tx_desc_link; // descriptor link list, the length of the link is determined by the copy buffer size
  45. mcp_dma_descriptor_t *rx_desc_link; // descriptor link list, the length of the link is determined by the copy buffer size
  46. intptr_t tx_start_desc_addr; // TX start descriptor address
  47. intptr_t rx_start_desc_addr; // RX start descriptor address
  48. intptr_t memcpy_dst_addr; // memcpy destination address
  49. size_t memcpy_size; // memcpy size
  50. async_memcpy_isr_cb_t cb; // user callback
  51. void *cb_args; // user callback args
  52. STAILQ_ENTRY(async_memcpy_transaction_t) idle_queue_entry; // Entry for the idle queue
  53. STAILQ_ENTRY(async_memcpy_transaction_t) ready_queue_entry; // Entry for the ready queue
  54. } async_memcpy_transaction_t;
  55. /// @brief Context of async memcpy driver
  56. /// @note - It saves two queues, one for idle transaction objects, one for ready transaction objects
  57. /// @note - Transaction objects are allocated from DMA-able memory
  58. /// @note - Number of transaction objects are determined by the backlog parameter
  59. typedef struct {
  60. async_memcpy_context_t parent; // Parent IO interface
  61. size_t sram_trans_align; // DMA transfer alignment (both in size and address) for SRAM memory
  62. size_t psram_trans_align; // DMA transfer alignment (both in size and address) for PSRAM memory
  63. size_t max_single_dma_buffer; // max DMA buffer size by a single descriptor
  64. int gdma_bus_id; // GDMA bus id (AHB, AXI, etc.)
  65. gdma_channel_handle_t tx_channel; // GDMA TX channel handle
  66. gdma_channel_handle_t rx_channel; // GDMA RX channel handle
  67. portMUX_TYPE spin_lock; // spin lock to avoid threads and isr from accessing the same resource simultaneously
  68. _Atomic async_memcpy_fsm_t fsm; // driver state machine, changing state should be atomic
  69. async_memcpy_transaction_t *transaction_pool; // transaction object pool
  70. STAILQ_HEAD(, async_memcpy_transaction_t) idle_queue_head; // Head of the idle queue
  71. STAILQ_HEAD(, async_memcpy_transaction_t) ready_queue_head; // Head of the ready queue
  72. } async_memcpy_gdma_context_t;
  73. static bool mcp_gdma_rx_eof_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data);
  74. static esp_err_t mcp_gdma_del(async_memcpy_context_t *ctx);
  75. static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *src, size_t n, async_memcpy_isr_cb_t cb_isr, void *cb_args);
  76. #if SOC_GDMA_SUPPORT_ETM
  77. static esp_err_t mcp_new_etm_event(async_memcpy_context_t *ctx, async_memcpy_etm_event_t event_type, esp_etm_event_handle_t *out_event);
  78. #endif // SOC_GDMA_SUPPORT_ETM
  79. static esp_err_t mcp_gdma_destroy(async_memcpy_gdma_context_t *mcp_gdma)
  80. {
  81. if (mcp_gdma->transaction_pool) {
  82. free(mcp_gdma->transaction_pool);
  83. }
  84. if (mcp_gdma->tx_channel) {
  85. gdma_disconnect(mcp_gdma->tx_channel);
  86. gdma_del_channel(mcp_gdma->tx_channel);
  87. }
  88. if (mcp_gdma->rx_channel) {
  89. gdma_disconnect(mcp_gdma->rx_channel);
  90. gdma_del_channel(mcp_gdma->rx_channel);
  91. }
  92. free(mcp_gdma);
  93. return ESP_OK;
  94. }
  95. static esp_err_t esp_async_memcpy_install_gdma_template(const async_memcpy_config_t *config, async_memcpy_handle_t *mcp,
  96. esp_err_t (*new_channel)(const gdma_channel_alloc_config_t *, gdma_channel_handle_t *), int gdma_bus_id)
  97. {
  98. esp_err_t ret = ESP_OK;
  99. async_memcpy_gdma_context_t *mcp_gdma = NULL;
  100. ESP_RETURN_ON_FALSE(config && mcp, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  101. // allocate memory of driver context from internal memory
  102. mcp_gdma = heap_caps_calloc(1, sizeof(async_memcpy_gdma_context_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
  103. ESP_GOTO_ON_FALSE(mcp_gdma, ESP_ERR_NO_MEM, err, TAG, "no mem for driver context");
  104. uint32_t trans_queue_len = config->backlog ? config->backlog : DEFAULT_TRANSACTION_QUEUE_LENGTH;
  105. // allocate memory for transaction pool
  106. mcp_gdma->transaction_pool = heap_caps_aligned_calloc(MCP_DMA_DESC_ALIGN, trans_queue_len, sizeof(async_memcpy_transaction_t),
  107. MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
  108. ESP_GOTO_ON_FALSE(mcp_gdma->transaction_pool, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction pool");
  109. // create TX channel and RX channel, they should reside in the same DMA pair
  110. gdma_channel_alloc_config_t tx_alloc_config = {
  111. .flags.reserve_sibling = 1,
  112. .direction = GDMA_CHANNEL_DIRECTION_TX,
  113. };
  114. ESP_GOTO_ON_ERROR(new_channel(&tx_alloc_config, &mcp_gdma->tx_channel), err, TAG, "failed to create GDMA TX channel");
  115. gdma_channel_alloc_config_t rx_alloc_config = {
  116. .direction = GDMA_CHANNEL_DIRECTION_RX,
  117. .sibling_chan = mcp_gdma->tx_channel,
  118. };
  119. ESP_GOTO_ON_ERROR(new_channel(&rx_alloc_config, &mcp_gdma->rx_channel), err, TAG, "failed to create GDMA RX channel");
  120. // initialize GDMA channels
  121. gdma_trigger_t m2m_trigger = GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_M2M, 0);
  122. // get a free DMA trigger ID for memory copy
  123. uint32_t free_m2m_id_mask = 0;
  124. gdma_get_free_m2m_trig_id_mask(mcp_gdma->tx_channel, &free_m2m_id_mask);
  125. m2m_trigger.instance_id = __builtin_ctz(free_m2m_id_mask);
  126. gdma_connect(mcp_gdma->rx_channel, m2m_trigger);
  127. gdma_connect(mcp_gdma->tx_channel, m2m_trigger);
  128. gdma_transfer_ability_t transfer_ability = {
  129. .sram_trans_align = config->sram_trans_align,
  130. .psram_trans_align = config->psram_trans_align,
  131. };
  132. ESP_GOTO_ON_ERROR(gdma_set_transfer_ability(mcp_gdma->tx_channel, &transfer_ability), err, TAG, "set tx trans ability failed");
  133. ESP_GOTO_ON_ERROR(gdma_set_transfer_ability(mcp_gdma->rx_channel, &transfer_ability), err, TAG, "set rx trans ability failed");
  134. // register rx eof callback
  135. gdma_rx_event_callbacks_t cbs = {
  136. .on_recv_eof = mcp_gdma_rx_eof_callback,
  137. };
  138. ESP_GOTO_ON_ERROR(gdma_register_rx_event_callbacks(mcp_gdma->rx_channel, &cbs, mcp_gdma), err, TAG, "failed to register RX EOF callback");
  139. // initialize transaction queue
  140. STAILQ_INIT(&mcp_gdma->idle_queue_head);
  141. STAILQ_INIT(&mcp_gdma->ready_queue_head);
  142. // pick transactions from the pool and insert to the idle queue
  143. for (int i = 0; i < trans_queue_len; i++) {
  144. STAILQ_INSERT_TAIL(&mcp_gdma->idle_queue_head, &mcp_gdma->transaction_pool[i], idle_queue_entry);
  145. }
  146. // initialize other members
  147. portMUX_INITIALIZE(&mcp_gdma->spin_lock);
  148. atomic_init(&mcp_gdma->fsm, MCP_FSM_IDLE);
  149. mcp_gdma->gdma_bus_id = gdma_bus_id;
  150. uint32_t psram_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
  151. uint32_t sram_cache_line_size = 0;
  152. #if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
  153. sram_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
  154. #endif
  155. // if the psram_trans_align is configured to zero, we should fall back to use the data cache line size
  156. size_t psram_trans_align = MAX(psram_cache_line_size, config->psram_trans_align);
  157. size_t sram_trans_align = MAX(sram_cache_line_size, config->sram_trans_align);
  158. size_t trans_align = MAX(sram_trans_align, psram_trans_align);
  159. mcp_gdma->max_single_dma_buffer = ALIGN_DOWN(DMA_DESCRIPTOR_BUFFER_MAX_SIZE, trans_align);
  160. mcp_gdma->psram_trans_align = psram_trans_align;
  161. mcp_gdma->sram_trans_align = sram_trans_align;
  162. mcp_gdma->parent.del = mcp_gdma_del;
  163. mcp_gdma->parent.memcpy = mcp_gdma_memcpy;
  164. #if SOC_GDMA_SUPPORT_ETM
  165. mcp_gdma->parent.new_etm_event = mcp_new_etm_event;
  166. #endif
  167. // return driver object
  168. *mcp = &mcp_gdma->parent;
  169. return ESP_OK;
  170. err:
  171. if (mcp_gdma) {
  172. mcp_gdma_destroy(mcp_gdma);
  173. }
  174. return ret;
  175. }
  176. #if SOC_AHB_GDMA_SUPPORTED
  177. esp_err_t esp_async_memcpy_install_gdma_ahb(const async_memcpy_config_t *config, async_memcpy_handle_t *mcp)
  178. {
  179. return esp_async_memcpy_install_gdma_template(config, mcp, gdma_new_ahb_channel, SOC_GDMA_BUS_AHB);
  180. }
  181. /// default installation falls back to use the AHB GDMA
  182. esp_err_t esp_async_memcpy_install(const async_memcpy_config_t *config, async_memcpy_handle_t *asmcp)
  183. __attribute__((alias("esp_async_memcpy_install_gdma_ahb")));
  184. #endif // SOC_AHB_GDMA_SUPPORTED
  185. #if SOC_AXI_GDMA_SUPPORTED
  186. esp_err_t esp_async_memcpy_install_gdma_axi(const async_memcpy_config_t *config, async_memcpy_handle_t *mcp)
  187. {
  188. return esp_async_memcpy_install_gdma_template(config, mcp, gdma_new_axi_channel, SOC_GDMA_BUS_AXI);
  189. }
  190. #endif // SOC_AXI_GDMA_SUPPORTED
  191. static esp_err_t mcp_gdma_del(async_memcpy_context_t *ctx)
  192. {
  193. async_memcpy_gdma_context_t *mcp_gdma = __containerof(ctx, async_memcpy_gdma_context_t, parent);
  194. // check if there are pending transactions
  195. ESP_RETURN_ON_FALSE(STAILQ_EMPTY(&mcp_gdma->ready_queue_head), ESP_ERR_INVALID_STATE, TAG, "there are pending transactions");
  196. // check if the driver is in IDLE state
  197. ESP_RETURN_ON_FALSE(atomic_load(&mcp_gdma->fsm) == MCP_FSM_IDLE, ESP_ERR_INVALID_STATE, TAG, "driver is not in IDLE state");
  198. return mcp_gdma_destroy(mcp_gdma);
  199. }
  200. static void mount_tx_buffer_to_dma(mcp_dma_descriptor_t *desc_array, int num_desc,
  201. uint8_t *buf, size_t buf_sz, size_t max_single_dma_buffer)
  202. {
  203. uint32_t prepared_length = 0;
  204. size_t len = buf_sz;
  205. for (int i = 0; i < num_desc - 1; i++) {
  206. desc_array[i].buffer = &buf[prepared_length];
  207. desc_array[i].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
  208. desc_array[i].dw0.suc_eof = 0;
  209. desc_array[i].dw0.size = max_single_dma_buffer;
  210. desc_array[i].dw0.length = max_single_dma_buffer;
  211. desc_array[i].next = &desc_array[i + 1];
  212. prepared_length += max_single_dma_buffer;
  213. len -= max_single_dma_buffer;
  214. }
  215. // take special care to the EOF descriptor
  216. desc_array[num_desc - 1].buffer = &buf[prepared_length];
  217. desc_array[num_desc - 1].next = NULL;
  218. desc_array[num_desc - 1].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
  219. desc_array[num_desc - 1].dw0.suc_eof = 1;
  220. desc_array[num_desc - 1].dw0.size = len;
  221. desc_array[num_desc - 1].dw0.length = len;
  222. #if MCP_NEEDS_WRITE_BACK_DESC_CACHE
  223. Cache_WriteBack_Addr(CACHE_MAP_L1_DCACHE, (uint32_t)desc_array, sizeof(mcp_dma_descriptor_t) * num_desc);
  224. #endif
  225. }
  226. static void mount_rx_buffer_to_dma(mcp_dma_descriptor_t *desc_array, int num_desc, mcp_dma_descriptor_t *eof_desc,
  227. uint8_t *buf, size_t buf_sz, size_t max_single_dma_buffer)
  228. {
  229. uint32_t prepared_length = 0;
  230. size_t len = buf_sz;
  231. if (desc_array) {
  232. assert(num_desc > 0);
  233. for (int i = 0; i < num_desc; i++) {
  234. desc_array[i].buffer = &buf[prepared_length];
  235. desc_array[i].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
  236. desc_array[i].dw0.size = max_single_dma_buffer;
  237. desc_array[i].dw0.length = max_single_dma_buffer;
  238. desc_array[i].next = &desc_array[i + 1];
  239. prepared_length += max_single_dma_buffer;
  240. len -= max_single_dma_buffer;
  241. }
  242. desc_array[num_desc - 1].next = eof_desc;
  243. }
  244. eof_desc->buffer = &buf[prepared_length];
  245. eof_desc->next = NULL;
  246. eof_desc->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
  247. eof_desc->dw0.size = len;
  248. eof_desc->dw0.length = len;
  249. #if MCP_NEEDS_WRITE_BACK_DESC_CACHE
  250. if (desc_array) {
  251. Cache_WriteBack_Addr(CACHE_MAP_L1_DCACHE, (uint32_t)desc_array, sizeof(mcp_dma_descriptor_t) * num_desc);
  252. }
  253. Cache_WriteBack_Addr(CACHE_MAP_L1_DCACHE, (uint32_t)eof_desc, sizeof(mcp_dma_descriptor_t));
  254. #endif
  255. }
  256. /// @brief help function to get one transaction from the ready queue
  257. /// @note this function is allowed to be called in ISR
  258. static async_memcpy_transaction_t *try_pop_trans_from_ready_queue(async_memcpy_gdma_context_t *mcp_gdma)
  259. {
  260. async_memcpy_transaction_t *trans = NULL;
  261. portENTER_CRITICAL_SAFE(&mcp_gdma->spin_lock);
  262. trans = STAILQ_FIRST(&mcp_gdma->ready_queue_head);
  263. if (trans) {
  264. STAILQ_REMOVE_HEAD(&mcp_gdma->ready_queue_head, ready_queue_entry);
  265. }
  266. portEXIT_CRITICAL_SAFE(&mcp_gdma->spin_lock);
  267. return trans;
  268. }
  269. /// @brief help function to start a pending transaction
  270. /// @note this function is allowed to be called in ISR
  271. static void try_start_pending_transaction(async_memcpy_gdma_context_t *mcp_gdma)
  272. {
  273. async_memcpy_fsm_t expected_fsm = MCP_FSM_IDLE;
  274. async_memcpy_transaction_t *trans = NULL;
  275. if (atomic_compare_exchange_strong(&mcp_gdma->fsm, &expected_fsm, MCP_FSM_RUN_WAIT)) {
  276. trans = try_pop_trans_from_ready_queue(mcp_gdma);
  277. if (trans) {
  278. atomic_store(&mcp_gdma->fsm, MCP_FSM_RUN);
  279. gdma_start(mcp_gdma->rx_channel, trans->rx_start_desc_addr);
  280. gdma_start(mcp_gdma->tx_channel, trans->tx_start_desc_addr);
  281. } else {
  282. atomic_store(&mcp_gdma->fsm, MCP_FSM_IDLE);
  283. }
  284. }
  285. }
  286. /// @brief help function to get one transaction from the idle queue
  287. /// @note this function is allowed to be called in ISR
  288. static async_memcpy_transaction_t *try_pop_trans_from_idle_queue(async_memcpy_gdma_context_t *mcp_gdma)
  289. {
  290. async_memcpy_transaction_t *trans = NULL;
  291. portENTER_CRITICAL_SAFE(&mcp_gdma->spin_lock);
  292. trans = STAILQ_FIRST(&mcp_gdma->idle_queue_head);
  293. if (trans) {
  294. STAILQ_REMOVE_HEAD(&mcp_gdma->idle_queue_head, idle_queue_entry);
  295. }
  296. portEXIT_CRITICAL_SAFE(&mcp_gdma->spin_lock);
  297. return trans;
  298. }
  299. static bool check_buffer_aligned(async_memcpy_gdma_context_t *mcp_gdma, void *src, void *dst, size_t n)
  300. {
  301. bool valid = true;
  302. uint32_t align_mask = 0;
  303. if (esp_ptr_external_ram(dst)) {
  304. if (mcp_gdma->psram_trans_align) {
  305. align_mask = mcp_gdma->psram_trans_align - 1;
  306. }
  307. } else {
  308. if (mcp_gdma->sram_trans_align) {
  309. align_mask = mcp_gdma->sram_trans_align - 1;
  310. }
  311. }
  312. // destination address must be cache line aligned
  313. valid = valid && (((uint32_t)dst & align_mask) == 0);
  314. valid = valid && ((n & align_mask) == 0);
  315. return valid;
  316. }
  317. static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *src, size_t n, async_memcpy_isr_cb_t cb_isr, void *cb_args)
  318. {
  319. esp_err_t ret = ESP_OK;
  320. async_memcpy_gdma_context_t *mcp_gdma = __containerof(ctx, async_memcpy_gdma_context_t, parent);
  321. // buffer location check
  322. #if SOC_AHB_GDMA_SUPPORTED && !SOC_AHB_GDMA_SUPPORT_PSRAM
  323. if (mcp_gdma->gdma_bus_id == SOC_GDMA_BUS_AHB) {
  324. ESP_RETURN_ON_FALSE(esp_ptr_internal(src) && esp_ptr_internal(dst), ESP_ERR_INVALID_ARG, TAG, "AHB GDMA can only access SRAM");
  325. }
  326. #endif // SOC_AHB_GDMA_SUPPORTED && !SOC_AHB_GDMA_SUPPORT_PSRAM
  327. #if SOC_AXI_GDMA_SUPPORTED && !SOC_AXI_GDMA_SUPPORT_PSRAM
  328. if (mcp_gdma->gdma_bus_id == SOC_GDMA_BUS_AXI) {
  329. ESP_RETURN_ON_FALSE(esp_ptr_internal(src) && esp_ptr_internal(dst), ESP_ERR_INVALID_ARG, TAG, "AXI_DMA can only access SRAM");
  330. }
  331. #endif // SOC_AXI_GDMA_SUPPORTED && !SOC_AXI_GDMA_SUPPORT_PSRAM
  332. // alignment check
  333. ESP_RETURN_ON_FALSE(check_buffer_aligned(mcp_gdma, src, dst, n), ESP_ERR_INVALID_ARG, TAG, "buffer not aligned: %p -> %p, sz=%zu", src, dst, n);
  334. async_memcpy_transaction_t *trans = NULL;
  335. // pick one transaction node from idle queue
  336. trans = try_pop_trans_from_idle_queue(mcp_gdma);
  337. // check if we get the transaction object successfully
  338. ESP_RETURN_ON_FALSE(trans, ESP_ERR_INVALID_STATE, TAG, "no free node in the idle queue");
  339. // calculate how many descriptors we want
  340. size_t max_single_dma_buffer = mcp_gdma->max_single_dma_buffer;
  341. uint32_t num_desc_per_path = (n + max_single_dma_buffer - 1) / max_single_dma_buffer;
  342. // allocate DMA descriptors, descriptors need a strict alignment
  343. trans->tx_desc_link = heap_caps_aligned_calloc(MCP_DMA_DESC_ALIGN, num_desc_per_path, sizeof(mcp_dma_descriptor_t),
  344. MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
  345. ESP_GOTO_ON_FALSE(trans->tx_desc_link, ESP_ERR_NO_MEM, err, TAG, "no mem for DMA descriptors");
  346. // don't have to allocate the EOF descriptor, we will use trans->eof_node as the RX EOF descriptor
  347. if (num_desc_per_path > 1) {
  348. trans->rx_desc_link = heap_caps_aligned_calloc(MCP_DMA_DESC_ALIGN, num_desc_per_path - 1, sizeof(mcp_dma_descriptor_t),
  349. MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
  350. ESP_GOTO_ON_FALSE(trans->rx_desc_link, ESP_ERR_NO_MEM, err, TAG, "no mem for DMA descriptors");
  351. } else {
  352. // small copy buffer, use the trans->eof_node is sufficient
  353. trans->rx_desc_link = NULL;
  354. }
  355. // (preload) mount src data to the TX descriptor
  356. mount_tx_buffer_to_dma(trans->tx_desc_link, num_desc_per_path, src, n, max_single_dma_buffer);
  357. // (preload) mount dst data to the RX descriptor
  358. mount_rx_buffer_to_dma(trans->rx_desc_link, num_desc_per_path - 1, &trans->eof_node, dst, n, max_single_dma_buffer);
  359. // if the data is in the cache, write back, then DMA can see the latest data
  360. #if MCP_NEEDS_WRITE_BACK_SRC_CACHE
  361. int write_back_map = CACHE_MAP_L1_DCACHE;
  362. if (esp_ptr_external_ram(src)) {
  363. write_back_map |= CACHE_MAP_L2_CACHE;
  364. }
  365. Cache_WriteBack_Addr(write_back_map, (uint32_t)src, n);
  366. #endif
  367. // save other transaction context
  368. trans->cb = cb_isr;
  369. trans->cb_args = cb_args;
  370. trans->memcpy_size = n;
  371. trans->memcpy_dst_addr = (intptr_t)dst;
  372. trans->tx_start_desc_addr = (intptr_t)trans->tx_desc_link;
  373. trans->rx_start_desc_addr = trans->rx_desc_link ? (intptr_t)trans->rx_desc_link : (intptr_t)&trans->eof_node;
  374. portENTER_CRITICAL(&mcp_gdma->spin_lock);
  375. // insert the trans to ready queue
  376. STAILQ_INSERT_TAIL(&mcp_gdma->ready_queue_head, trans, ready_queue_entry);
  377. portEXIT_CRITICAL(&mcp_gdma->spin_lock);
  378. // check driver state, if there's no running transaction, start a new one
  379. try_start_pending_transaction(mcp_gdma);
  380. return ESP_OK;
  381. err:
  382. if (trans) {
  383. if (trans->tx_desc_link) {
  384. free(trans->tx_desc_link);
  385. trans->tx_desc_link = NULL;
  386. }
  387. if (trans->rx_desc_link) {
  388. free(trans->rx_desc_link);
  389. trans->rx_desc_link = NULL;
  390. }
  391. // return back the trans to idle queue
  392. portENTER_CRITICAL(&mcp_gdma->spin_lock);
  393. STAILQ_INSERT_TAIL(&mcp_gdma->idle_queue_head, trans, idle_queue_entry);
  394. portEXIT_CRITICAL(&mcp_gdma->spin_lock);
  395. }
  396. return ret;
  397. }
  398. static bool mcp_gdma_rx_eof_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
  399. {
  400. bool need_yield = false;
  401. async_memcpy_gdma_context_t *mcp_gdma = (async_memcpy_gdma_context_t *)user_data;
  402. mcp_dma_descriptor_t *eof_desc = (mcp_dma_descriptor_t *)event_data->rx_eof_desc_addr;
  403. // get the transaction object address by the EOF descriptor address
  404. async_memcpy_transaction_t *trans = __containerof(eof_desc, async_memcpy_transaction_t, eof_node);
  405. // switch driver state from RUN to IDLE
  406. async_memcpy_fsm_t expected_fsm = MCP_FSM_RUN;
  407. if (atomic_compare_exchange_strong(&mcp_gdma->fsm, &expected_fsm, MCP_FSM_IDLE_WAIT)) {
  408. // if the data is in the cache, invalidate, then CPU can see the latest data
  409. #if MCP_NEEDS_INVALIDATE_DST_CACHE
  410. int invalidate_map = CACHE_MAP_L1_DCACHE;
  411. if (esp_ptr_external_ram((const void *)trans->memcpy_dst_addr)) {
  412. invalidate_map |= CACHE_MAP_L2_CACHE;
  413. }
  414. Cache_Invalidate_Addr(invalidate_map, (uint32_t)trans->memcpy_dst_addr, trans->memcpy_size);
  415. #endif
  416. // invoked callback registered by user
  417. async_memcpy_isr_cb_t cb = trans->cb;
  418. if (cb) {
  419. async_memcpy_event_t e = {
  420. // No event data for now
  421. };
  422. need_yield = cb(&mcp_gdma->parent, &e, trans->cb_args);
  423. }
  424. // recycle descriptor memory
  425. if (trans->tx_desc_link) {
  426. free(trans->tx_desc_link);
  427. trans->tx_desc_link = NULL;
  428. }
  429. if (trans->rx_desc_link) {
  430. free(trans->rx_desc_link);
  431. trans->rx_desc_link = NULL;
  432. }
  433. trans->cb = NULL;
  434. portENTER_CRITICAL_ISR(&mcp_gdma->spin_lock);
  435. // insert the trans object to the idle queue
  436. STAILQ_INSERT_TAIL(&mcp_gdma->idle_queue_head, trans, idle_queue_entry);
  437. portEXIT_CRITICAL_ISR(&mcp_gdma->spin_lock);
  438. atomic_store(&mcp_gdma->fsm, MCP_FSM_IDLE);
  439. }
  440. // try start the next pending transaction
  441. try_start_pending_transaction(mcp_gdma);
  442. return need_yield;
  443. }
  444. #if SOC_GDMA_SUPPORT_ETM
  445. static esp_err_t mcp_new_etm_event(async_memcpy_context_t *ctx, async_memcpy_etm_event_t event_type, esp_etm_event_handle_t *out_event)
  446. {
  447. async_memcpy_gdma_context_t *mcp_gdma = __containerof(ctx, async_memcpy_gdma_context_t, parent);
  448. if (event_type == ASYNC_MEMCPY_ETM_EVENT_COPY_DONE) {
  449. // use the RX EOF to indicate the async memcpy done event
  450. gdma_etm_event_config_t etm_event_conf = {
  451. .event_type = GDMA_ETM_EVENT_EOF,
  452. };
  453. return gdma_new_etm_event(mcp_gdma->rx_channel, &etm_event_conf, out_event);
  454. } else {
  455. return ESP_ERR_NOT_SUPPORTED;
  456. }
  457. }
  458. #endif // SOC_GDMA_SUPPORT_ETM