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