drv_dma.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /*
  2. * Copyright (c) 2006-2026, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2026-04-13 wdfk-prog Add STM32 DMA common helpers
  9. */
  10. /**
  11. * @file drv_dma.c
  12. * @brief STM32 DMA common helper layer for peripheral drivers.
  13. */
  14. #include "drv_dma.h"
  15. // #define DRV_DEBUG
  16. #define LOG_TAG "drv.dma"
  17. #include <drv_log.h>
  18. /*
  19. * DMA-capable BSPs are expected to enable HAL_DMA_MODULE_ENABLED in the
  20. * STM32 HAL configuration, so keep the common DMA helper in the build.
  21. */
  22. #ifdef HAL_DMA_MODULE_ENABLED
  23. #if defined(STM32_DMA_USES_REQUEST)
  24. /**
  25. * @brief Enable the DMAMUX clock when the current STM32 DMA path needs it.
  26. */
  27. static void stm32_dma_enable_dmamux_clock(void)
  28. {
  29. #if defined(DMAMUX1) && defined(__HAL_RCC_DMAMUX1_CLK_ENABLE)
  30. __HAL_RCC_DMAMUX1_CLK_ENABLE();
  31. #elif defined(DMAMUX) && defined(__HAL_RCC_DMAMUX_CLK_ENABLE)
  32. __HAL_RCC_DMAMUX_CLK_ENABLE();
  33. #endif /* defined(DMAMUX1) && defined(__HAL_RCC_DMAMUX1_CLK_ENABLE) */
  34. }
  35. #endif /* defined(STM32_DMA_USES_REQUEST) */
  36. /**
  37. * @brief Enable the clock of one DMA controller and wait for the write to complete.
  38. * @param dma_rcc RCC enable bit of the DMA controller.
  39. */
  40. static void stm32_dma_enable_clock(rt_uint32_t dma_rcc)
  41. {
  42. rt_uint32_t tmpreg = 0x00U;
  43. #if defined(STM32_DMA_USES_RCC_AHBENR)
  44. SET_BIT(RCC->AHBENR, dma_rcc);
  45. tmpreg = READ_BIT(RCC->AHBENR, dma_rcc);
  46. #elif defined(STM32_DMA_USES_RCC_MP_AHB2ENSETR)
  47. SET_BIT(RCC->MP_AHB2ENSETR, dma_rcc);
  48. tmpreg = READ_BIT(RCC->MP_AHB2ENSETR, dma_rcc);
  49. #elif defined(STM32_DMA_USES_RCC_AHB1ENR)
  50. SET_BIT(RCC->AHB1ENR, dma_rcc);
  51. tmpreg = READ_BIT(RCC->AHB1ENR, dma_rcc);
  52. #endif /* defined(STM32_DMA_USES_RCC_AHBENR) || defined(STM32_DMA_USES_RCC_MP_AHB2ENSETR) || defined(STM32_DMA_USES_RCC_AHB1ENR) */
  53. #if defined(STM32_DMA_USES_REQUEST)
  54. stm32_dma_enable_dmamux_clock();
  55. #endif /* defined(STM32_DMA_USES_REQUEST) */
  56. UNUSED(tmpreg);
  57. }
  58. /* Only a few STM32 families expose DMA requests on shared NVIC lines.
  59. * Use reference counting only for those known shared IRQ numbers so one
  60. * DMA client does not disable a line still used by another active client.
  61. * All other DMA IRQs keep the direct enable/disable behavior.
  62. */
  63. #if (defined(SOC_SERIES_STM32F1) && defined(DMA2_Channel4_5_IRQn)) \
  64. || (defined(SOC_SERIES_STM32L0) && defined(DMA1_Channel4_5_6_7_IRQn)) \
  65. || (defined(SOC_SERIES_STM32G0) && defined(DMA1_Channel2_3_IRQn)) \
  66. || (defined(SOC_SERIES_STM32F0) && (defined(DMA1_Channel2_3_IRQn) || defined(DMA1_Channel4_5_IRQn) || defined(DMA1_Channel4_5_6_7_IRQn)))
  67. #define STM32_DMA_HAS_SHARED_IRQ_REFCNT
  68. #define STM32_DMA_IRQ_SLOT_COUNT ((rt_uint32_t)(sizeof(NVIC->ISER) / sizeof(NVIC->ISER[0]) * 32U))
  69. /**
  70. * @brief Reference count for each shared DMA IRQ line.
  71. */
  72. static rt_uint16_t stm32_dma_irq_ref_count[STM32_DMA_IRQ_SLOT_COUNT];
  73. /**
  74. * @brief Check whether one DMA IRQ number can index the local reference table.
  75. * @param dma_irq DMA IRQ number to validate.
  76. * @retval RT_TRUE The IRQ number maps to one valid table slot.
  77. * @retval RT_FALSE The IRQ number is negative or outside the table range.
  78. */
  79. static rt_bool_t stm32_dma_irq_is_valid(IRQn_Type dma_irq)
  80. {
  81. return ((int32_t)dma_irq >= 0) && ((rt_uint32_t)dma_irq < STM32_DMA_IRQ_SLOT_COUNT);
  82. }
  83. /**
  84. * @brief Check whether one DMA IRQ line is shared and needs reference counting.
  85. * @param dma_irq DMA IRQ number to inspect.
  86. * @retval RT_TRUE The IRQ line is shared by multiple DMA endpoints.
  87. * @retval RT_FALSE The IRQ line can use direct enable and disable handling.
  88. */
  89. static rt_bool_t stm32_dma_irq_needs_refcount(IRQn_Type dma_irq)
  90. {
  91. #if defined(SOC_SERIES_STM32F1) && defined(DMA2_Channel4_5_IRQn)
  92. if (dma_irq == DMA2_Channel4_5_IRQn)
  93. {
  94. return RT_TRUE;
  95. }
  96. #endif /* defined(SOC_SERIES_STM32F1) && defined(DMA2_Channel4_5_IRQn) */
  97. #if defined(SOC_SERIES_STM32L0) && defined(DMA1_Channel4_5_6_7_IRQn)
  98. if (dma_irq == DMA1_Channel4_5_6_7_IRQn)
  99. {
  100. return RT_TRUE;
  101. }
  102. #endif /* defined(SOC_SERIES_STM32L0) && defined(DMA1_Channel4_5_6_7_IRQn) */
  103. #if defined(SOC_SERIES_STM32G0) && defined(DMA1_Channel2_3_IRQn)
  104. if (dma_irq == DMA1_Channel2_3_IRQn)
  105. {
  106. return RT_TRUE;
  107. }
  108. #endif /* defined(SOC_SERIES_STM32G0) && defined(DMA1_Channel2_3_IRQn) */
  109. #if defined(SOC_SERIES_STM32F0)
  110. # if defined(DMA1_Channel2_3_IRQn)
  111. if (dma_irq == DMA1_Channel2_3_IRQn)
  112. {
  113. return RT_TRUE;
  114. }
  115. # endif /* defined(DMA1_Channel2_3_IRQn) */
  116. # if defined(DMA1_Channel4_5_IRQn)
  117. if (dma_irq == DMA1_Channel4_5_IRQn)
  118. {
  119. return RT_TRUE;
  120. }
  121. # endif /* defined(DMA1_Channel4_5_IRQn) */
  122. # if defined(DMA1_Channel4_5_6_7_IRQn)
  123. if (dma_irq == DMA1_Channel4_5_6_7_IRQn)
  124. {
  125. return RT_TRUE;
  126. }
  127. # endif /* defined(DMA1_Channel4_5_6_7_IRQn) */
  128. #endif /* defined(SOC_SERIES_STM32F0) */
  129. return RT_FALSE;
  130. }
  131. #endif /* shared DMA IRQ families */
  132. /**
  133. * @brief Enable one DMA IRQ line and apply the requested NVIC priority.
  134. * @param dma_irq DMA IRQ number to enable.
  135. * @param preempt_priority NVIC preempt priority for the DMA IRQ.
  136. * @param sub_priority NVIC subpriority for the DMA IRQ.
  137. */
  138. static void stm32_dma_irq_get(IRQn_Type dma_irq,
  139. rt_uint8_t preempt_priority,
  140. rt_uint8_t sub_priority)
  141. {
  142. #if defined(STM32_DMA_HAS_SHARED_IRQ_REFCNT)
  143. rt_base_t level;
  144. if (stm32_dma_irq_needs_refcount(dma_irq) && stm32_dma_irq_is_valid(dma_irq))
  145. {
  146. level = rt_hw_interrupt_disable();
  147. if (stm32_dma_irq_ref_count[(rt_uint32_t)dma_irq] == 0U)
  148. {
  149. HAL_NVIC_SetPriority(dma_irq, preempt_priority, sub_priority);
  150. HAL_NVIC_EnableIRQ(dma_irq);
  151. }
  152. stm32_dma_irq_ref_count[(rt_uint32_t)dma_irq]++;
  153. rt_hw_interrupt_enable(level);
  154. return;
  155. }
  156. #endif /* defined(STM32_DMA_HAS_SHARED_IRQ_REFCNT) */
  157. HAL_NVIC_SetPriority(dma_irq, preempt_priority, sub_priority);
  158. HAL_NVIC_EnableIRQ(dma_irq);
  159. }
  160. /**
  161. * @brief Release one DMA IRQ line and disable it when no user remains.
  162. * @param dma_irq DMA IRQ number to release.
  163. */
  164. static void stm32_dma_irq_put(IRQn_Type dma_irq)
  165. {
  166. #if defined(STM32_DMA_HAS_SHARED_IRQ_REFCNT)
  167. rt_base_t level;
  168. if (stm32_dma_irq_needs_refcount(dma_irq) && stm32_dma_irq_is_valid(dma_irq))
  169. {
  170. level = rt_hw_interrupt_disable();
  171. if (stm32_dma_irq_ref_count[(rt_uint32_t)dma_irq] > 0U)
  172. {
  173. stm32_dma_irq_ref_count[(rt_uint32_t)dma_irq]--;
  174. if (stm32_dma_irq_ref_count[(rt_uint32_t)dma_irq] == 0U)
  175. {
  176. HAL_NVIC_DisableIRQ(dma_irq);
  177. }
  178. }
  179. rt_hw_interrupt_enable(level);
  180. return;
  181. }
  182. #endif /* defined(STM32_DMA_HAS_SHARED_IRQ_REFCNT) */
  183. HAL_NVIC_DisableIRQ(dma_irq);
  184. }
  185. /**
  186. * @brief Copy one static DMA descriptor into one HAL DMA handle.
  187. * @param dma_handle DMA handle to update.
  188. * @param dma_config Static DMA endpoint description.
  189. */
  190. static void stm32_dma_apply_config(DMA_HandleTypeDef *dma_handle,
  191. const struct stm32_dma_config *dma_config)
  192. {
  193. dma_handle->Instance = dma_config->Instance;
  194. #if defined(STM32_DMA_USES_GPDMA)
  195. dma_handle->Init.Request = dma_config->request;
  196. dma_handle->Init.BlkHWRequest = dma_config->blk_hw_request;
  197. dma_handle->Init.Direction = dma_config->direction;
  198. dma_handle->Init.SrcInc = dma_config->src_inc;
  199. dma_handle->Init.DestInc = dma_config->dest_inc;
  200. dma_handle->Init.SrcDataWidth = dma_config->src_data_width;
  201. dma_handle->Init.DestDataWidth = dma_config->dest_data_width;
  202. dma_handle->Init.Priority = dma_config->priority;
  203. dma_handle->Init.SrcBurstLength = dma_config->src_burst_length;
  204. dma_handle->Init.DestBurstLength = dma_config->dest_burst_length;
  205. dma_handle->Init.TransferAllocatedPort = dma_config->transfer_allocated_port;
  206. dma_handle->Init.TransferEventMode = dma_config->transfer_event_mode;
  207. dma_handle->Init.Mode = dma_config->mode;
  208. #else
  209. #if defined(STM32_DMA_USES_CHANNEL)
  210. dma_handle->Init.Channel = dma_config->channel;
  211. #endif /* defined(STM32_DMA_USES_CHANNEL) */
  212. #if defined(STM32_DMA_USES_REQUEST)
  213. dma_handle->Init.Request = dma_config->request;
  214. #endif /* defined(STM32_DMA_USES_REQUEST) */
  215. dma_handle->Init.Direction = dma_config->direction;
  216. dma_handle->Init.PeriphInc = dma_config->periph_inc;
  217. dma_handle->Init.MemInc = dma_config->mem_inc;
  218. dma_handle->Init.PeriphDataAlignment = dma_config->periph_data_alignment;
  219. dma_handle->Init.MemDataAlignment = dma_config->mem_data_alignment;
  220. dma_handle->Init.Mode = dma_config->mode;
  221. dma_handle->Init.Priority = dma_config->priority;
  222. #if defined(STM32_DMA_SUPPORTS_FIFO)
  223. dma_handle->Init.FIFOMode = dma_config->fifo_mode;
  224. dma_handle->Init.FIFOThreshold = dma_config->fifo_threshold;
  225. dma_handle->Init.MemBurst = dma_config->mem_burst;
  226. dma_handle->Init.PeriphBurst = dma_config->periph_burst;
  227. #endif /* defined(STM32_DMA_SUPPORTS_FIFO) */
  228. #endif /* defined(STM32_DMA_USES_GPDMA) */
  229. }
  230. /**
  231. * @brief Enable one DMA controller, apply the static descriptor and initialize HAL state.
  232. * @param dma_handle DMA handle owned by one peripheral driver.
  233. * @param dma_config Board-level DMA endpoint description.
  234. * @retval RT_EOK Initialization succeeded.
  235. * @retval -RT_ERROR HAL initialization failed.
  236. */
  237. rt_err_t stm32_dma_init(DMA_HandleTypeDef *dma_handle,
  238. const struct stm32_dma_config *dma_config)
  239. {
  240. RT_ASSERT(dma_handle != RT_NULL);
  241. RT_ASSERT(dma_config != RT_NULL);
  242. stm32_dma_enable_clock(dma_config->dma_rcc);
  243. stm32_dma_apply_config(dma_handle, dma_config);
  244. LOG_D("dma init, dma=%p, irq=%d", dma_handle->Instance, dma_config->dma_irq);
  245. if (HAL_DMA_DeInit(dma_handle) != HAL_OK)
  246. {
  247. LOG_E("dma deinit failed, dma=%p, irq=%d", dma_handle->Instance, dma_config->dma_irq);
  248. return -RT_ERROR;
  249. }
  250. if (HAL_DMA_Init(dma_handle) != HAL_OK)
  251. {
  252. LOG_E("dma init failed, dma=%p, irq=%d", dma_handle->Instance, dma_config->dma_irq);
  253. return -RT_ERROR;
  254. }
  255. return RT_EOK;
  256. }
  257. /**
  258. * @brief Initialize one DMA handle, attach it to the parent HAL handle and enable the DMA IRQ.
  259. * @param dma_handle DMA handle owned by one peripheral driver.
  260. * @param parent_handle Parent HAL handle, such as UART_HandleTypeDef or SPI_HandleTypeDef.
  261. * @param dma_slot Address of the parent handle DMA slot, such as &huart->hdmarx.
  262. * @param dma_config Board-level DMA endpoint description.
  263. * @retval RT_EOK Initialization succeeded.
  264. * @retval -RT_ERROR HAL initialization failed.
  265. */
  266. rt_err_t stm32_dma_setup(DMA_HandleTypeDef *dma_handle,
  267. void *parent_handle,
  268. DMA_HandleTypeDef **dma_slot,
  269. const struct stm32_dma_config *dma_config)
  270. {
  271. rt_err_t result;
  272. result = stm32_dma_init(dma_handle, dma_config);
  273. if (result != RT_EOK)
  274. {
  275. return result;
  276. }
  277. if ((parent_handle != RT_NULL) && (dma_slot != RT_NULL))
  278. {
  279. *dma_slot = dma_handle;
  280. dma_handle->Parent = parent_handle;
  281. }
  282. stm32_dma_irq_get(dma_config->dma_irq, dma_config->preempt_priority, dma_config->sub_priority);
  283. LOG_D("dma setup, dma=%p, irq=%d", dma_handle->Instance, dma_config->dma_irq);
  284. return RT_EOK;
  285. }
  286. /**
  287. * @brief Disable one DMA IRQ, optionally abort the current transfer and de-initialize HAL state.
  288. * @param dma_handle DMA handle owned by one peripheral driver.
  289. * @param dma_config Board-level DMA endpoint description.
  290. * @param abort_first RT_TRUE aborts the ongoing transfer before HAL_DMA_DeInit().
  291. * @retval RT_EOK De-initialization succeeded.
  292. * @retval -RT_ERROR HAL de-initialization failed.
  293. */
  294. rt_err_t stm32_dma_deinit(DMA_HandleTypeDef *dma_handle,
  295. const struct stm32_dma_config *dma_config,
  296. rt_bool_t abort_first)
  297. {
  298. RT_ASSERT(dma_handle != RT_NULL);
  299. RT_ASSERT(dma_config != RT_NULL);
  300. stm32_dma_irq_put(dma_config->dma_irq);
  301. LOG_D("dma deinit, dma=%p, irq=%d", dma_handle->Instance, dma_config->dma_irq);
  302. if (abort_first)
  303. {
  304. if (HAL_DMA_Abort(dma_handle) != HAL_OK)
  305. {
  306. LOG_W("dma abort failed, continue deinit, dma=%p, irq=%d", dma_handle->Instance, dma_config->dma_irq);
  307. }
  308. }
  309. if (HAL_DMA_DeInit(dma_handle) != HAL_OK)
  310. {
  311. LOG_E("dma deinit failed, dma=%p, irq=%d", dma_handle->Instance, dma_config->dma_irq);
  312. return -RT_ERROR;
  313. }
  314. return RT_EOK;
  315. }
  316. #endif /* HAL_DMA_MODULE_ENABLED */