1 /*
2  * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 // DO NOT USE THESE APIS IN ANY APPLICATIONS
8 // GDMA driver is not public for end users, but for ESP-IDF developers.
9 
10 #pragma once
11 
12 #include <stdbool.h>
13 #include "esp_etm.h"
14 #include "soc/gdma_channel.h"
15 #include "hal/gdma_types.h"
16 #include "esp_err.h"
17 
18 #ifdef __cplusplus
19 extern "C" {
20 #endif
21 
22 /**
23  * @brief Type of GDMA channel handle
24  *
25  */
26 typedef struct gdma_channel_t *gdma_channel_handle_t;
27 
28 /**
29  * @brief Collection of configuration items that used for allocating GDMA channel
30  *
31  */
32 typedef struct {
33     gdma_channel_handle_t sibling_chan; /*!< DMA sibling channel handle (NULL means having sibling is not necessary) */
34     gdma_channel_direction_t direction; /*!< DMA channel direction */
35     struct {
36         int reserve_sibling: 1; /*!< If set, DMA channel allocator would prefer to allocate new channel in a new pair, and reserve sibling channel for future use */
37     } flags;
38 } gdma_channel_alloc_config_t;
39 
40 /**
41  * @brief GDMA transfer ability
42  *
43  * @note The alignment set in this structure is **not** a guarantee that gdma driver will take care of the nonalignment cases.
44  *       Actually the GDMA driver has no knowledge about the DMA buffer (address and size) used by upper layer.
45  *       So it's the responsibility of the **upper layer** to take care of the buffer address and size.
46  *
47  */
48 typedef struct {
49     size_t sram_trans_align;  /*!< DMA transfer alignment for memory in SRAM, in bytes. The driver enables/disables burst mode based on this value. 0 means no alignment is required */
50     size_t psram_trans_align; /*!< DMA transfer alignment for memory in PSRAM, in bytes. The driver sets proper burst block size based on the alignment value. 0 means no alignment is required */
51 } gdma_transfer_ability_t;
52 
53 /**
54  * @brief Type of GDMA event data
55  *
56  */
57 typedef struct {
58     union {
59         intptr_t rx_eof_desc_addr; /*!< EOF descriptor address of RX channel */
60         intptr_t tx_eof_desc_addr; /*!< EOF descriptor address of TX channel */
61     };
62 } gdma_event_data_t;
63 
64 /**
65  * @brief Type of GDMA event callback
66  * @param dma_chan GDMA channel handle, created from `gdma_new_channel`
67  * @param event_data GDMA event data
68  * @param user_data User registered data from `gdma_register_tx_event_callbacks` or `gdma_register_rx_event_callbacks`
69  *
70  * @return Whether a task switch is needed after the callback function returns,
71  *         this is usually due to the callback wakes up some high priority task.
72  *
73  */
74 typedef bool (*gdma_event_callback_t)(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data);
75 
76 /**
77  * @brief Group of supported GDMA TX callbacks
78  * @note The callbacks are all running under ISR environment
79  *
80  */
81 typedef struct {
82     gdma_event_callback_t on_trans_eof; /*!< Invoked when TX engine meets EOF descriptor */
83 } gdma_tx_event_callbacks_t;
84 
85 /**
86  * @brief Group of supported GDMA RX callbacks
87  * @note The callbacks are all running under ISR environment
88  *
89  */
90 typedef struct {
91     gdma_event_callback_t on_recv_eof; /*!< Invoked when RX engine meets EOF descriptor */
92 } gdma_rx_event_callbacks_t;
93 
94 /**
95  * @brief Type of GDMA engine trigger
96  * @note It's recommended to initialize this structure with `GDMA_MAKE_TRIGGER`.
97  *
98  */
99 typedef struct {
100     gdma_trigger_peripheral_t periph; /*!< Target peripheral which will trigger DMA operations */
101     int instance_id;                  /*!< Peripheral instance ID. Supported IDs are listed in `soc/gdma_channel.h`, e.g. SOC_GDMA_TRIG_PERIPH_UHCI0 */
102 } gdma_trigger_t;
103 
104 /**
105  * @brief Helper macro to initialize GDMA trigger
106  * @note value of `peri` must be selected from `gdma_trigger_peripheral_t` enum.
107  *       e.g. GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_I2S,0)
108  *
109  */
110 #define GDMA_MAKE_TRIGGER(peri, id) \
111     (gdma_trigger_t) { .periph = peri, .instance_id = SOC_##peri##id }
112 
113 /**
114  * @brief A collection of strategy item that each GDMA channel could apply
115  *
116  */
117 typedef struct {
118     bool owner_check;      /*!< If set / clear, DMA channel enables / disables checking owner validity */
119     bool auto_update_desc; /*!< If set / clear, DMA channel enables / disables hardware to update descriptor automatically (TX channel only) */
120 } gdma_strategy_config_t;
121 
122 /**
123  * @brief Create GDMA channel
124  * @note This API won't install interrupt service for the allocated channel.
125  *       If interrupt service is needed, user has to register GDMA event callback by `gdma_register_tx_event_callbacks` or `gdma_register_rx_event_callbacks`.
126  *
127  * @param[in] config Pointer to a collection of configurations for allocating GDMA channel
128  * @param[out] ret_chan Returnned channel handle
129  * @return
130  *      - ESP_OK: Create DMA channel successfully
131  *      - ESP_ERR_INVALID_ARG: Create DMA channel failed because of invalid argument
132  *      - ESP_ERR_NO_MEM: Create DMA channel failed because out of memory
133  *      - ESP_FAIL: Create DMA channel failed because of other error
134  */
135 esp_err_t gdma_new_channel(const gdma_channel_alloc_config_t *config, gdma_channel_handle_t *ret_chan);
136 
137 /**
138  * @brief Connect GDMA channel to trigger peripheral
139  *
140  * @note Suggest to use helper macro `GDMA_MAKE_TRIGGER` to construct parameter `trig_periph`. e.g. GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_SHA,0)
141  * @note Connecting to a peripheral will also reset the DMA FIFO and FSM automatically
142  *
143  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
144  * @param[in] trig_periph GDMA trigger peripheral
145  * @return
146  *      - ESP_OK: Connect GDMA channel successfully
147  *      - ESP_ERR_INVALID_ARG: Connect GDMA channel failed because of invalid argument
148  *      - ESP_ERR_INVALID_STATE: Connect GDMA channel failed because DMA channel is working with another peripheral
149  *      - ESP_FAIL: Connect GDMA channel failed because of other error
150  */
151 esp_err_t gdma_connect(gdma_channel_handle_t dma_chan, gdma_trigger_t trig_periph);
152 
153 /**
154  * @brief Disconnect GMA channel from peripheral
155  *
156  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
157  * @return
158  *      - ESP_OK: Disconnect GDMA channel successfully
159  *      - ESP_ERR_INVALID_ARG: Disconnect GDMA channel failed because of invalid argument
160  *      - ESP_ERR_INVALID_STATE: Disconnect GDMA channel failed because DMA channel is not connected to any peripheral
161  *      - ESP_FAIL: Disconnect DMA channel failed because of other error
162  */
163 esp_err_t gdma_disconnect(gdma_channel_handle_t dma_chan);
164 
165 /**
166  * @brief Set DMA channel transfer ability
167  *
168  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
169  * @param[in] ability Transfer ability, e.g. alignment
170  * @return
171  *      - ESP_OK: Set DMA channel transfer ability successfully
172  *      - ESP_ERR_INVALID_ARG: Set DMA channel transfer ability failed because of invalid argument
173  *      - ESP_FAIL: Set DMA channel transfer ability failed because of other error
174  */
175 esp_err_t gdma_set_transfer_ability(gdma_channel_handle_t dma_chan, const gdma_transfer_ability_t *ability);
176 
177 /**
178  * @brief Apply channel strategy for GDMA channel
179  *
180  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
181  * @param[in] config Configuration of GDMA channel strategy
182  *      - ESP_OK: Apply channel strategy successfully
183  *      - ESP_ERR_INVALID_ARG: Apply channel strategy failed because of invalid argument
184  *      - ESP_FAIL: Apply channel strategy failed because of other error
185  */
186 esp_err_t gdma_apply_strategy(gdma_channel_handle_t dma_chan, const gdma_strategy_config_t *config);
187 
188 /**
189  * @brief Set GDMA channel priority
190  *
191  * @note By default, all GDMA channels are with the same priority: 0. Channels with the same priority are served in round-robin manner.
192  *
193  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
194  * @param[in] priority Priority of GDMA channel, higher value means higher priority
195  * @return
196  *      - ESP_OK: Set GDMA channel priority successfully
197  *      - ESP_ERR_INVALID_ARG: Set GDMA channel priority failed because of invalid argument, e.g. priority out of range [0,GDMA_LL_CHANNEL_MAX_PRIORITY]
198  *      - ESP_FAIL: Set GDMA channel priority failed because of other error
199  */
200 esp_err_t gdma_set_priority(gdma_channel_handle_t dma_chan, uint32_t priority);
201 
202 /**
203  * @brief Delete GDMA channel
204  * @note If you call `gdma_new_channel` several times for a same peripheral, make sure you call this API the same times.
205  *
206  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
207  * @return
208  *      - ESP_OK: Delete GDMA channel successfully
209  *      - ESP_ERR_INVALID_ARG: Delete GDMA channel failed because of invalid argument
210  *      - ESP_FAIL: Delete GDMA channel failed because of other error
211  */
212 esp_err_t gdma_del_channel(gdma_channel_handle_t dma_chan);
213 
214 /**
215  * @brief Get the channel ID
216  *
217  * @note This API breaks the encapsulation of GDMA Channel Object.
218  *       With the returned channel ID, you can even bypass all other GDMA driver API and access Low Level API directly.
219  *
220  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
221  * @param[out] channel_id Returned channel ID
222  * @return
223  *      - ESP_OK: Get GDMA channel ID successfully
224  *      - ESP_ERR_INVALID_ARG: Get GDMA channel ID failed because of invalid argument
225  *      - ESP_FAIL: Get GDMA channel ID failed because of other error
226  */
227 esp_err_t gdma_get_channel_id(gdma_channel_handle_t dma_chan, int *channel_id);
228 
229 /**
230  * @brief Set GDMA event callbacks for TX channel
231  * @note This API will install GDMA interrupt service for the channel internally
232  *
233  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
234  * @param[in] cbs Group of callback functions
235  * @param[in] user_data User data, which will be passed to callback functions directly
236  * @return
237  *      - ESP_OK: Set event callbacks successfully
238  *      - ESP_ERR_INVALID_ARG: Set event callbacks failed because of invalid argument
239  *      - ESP_FAIL: Set event callbacks failed because of other error
240  */
241 esp_err_t gdma_register_tx_event_callbacks(gdma_channel_handle_t dma_chan, gdma_tx_event_callbacks_t *cbs, void *user_data);
242 
243 /**
244  * @brief Set GDMA event callbacks for RX channel
245  * @note This API will install GDMA interrupt service for the channel internally
246  *
247  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
248  * @param[in] cbs Group of callback functions
249  * @param[in] user_data User data, which will be passed to callback functions directly
250  * @return
251  *      - ESP_OK: Set event callbacks successfully
252  *      - ESP_ERR_INVALID_ARG: Set event callbacks failed because of invalid argument
253  *      - ESP_FAIL: Set event callbacks failed because of other error
254  */
255 esp_err_t gdma_register_rx_event_callbacks(gdma_channel_handle_t dma_chan, gdma_rx_event_callbacks_t *cbs, void *user_data);
256 
257 /**
258  * @brief Set DMA descriptor address and start engine
259  *
260  * @note This function is allowed to run within ISR context
261  * @note This function is also allowed to run when Cache is disabled, if `CONFIG_GDMA_CTRL_FUNC_IN_IRAM` is enabled
262  *
263  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
264  * @param[in] desc_base_addr Base address of descriptors (usually the descriptors are chained into a link or ring)
265  * @return
266  *      - ESP_OK: Start DMA engine successfully
267  *      - ESP_ERR_INVALID_ARG: Start DMA engine failed because of invalid argument
268  *      - ESP_ERR_INVALID_STATE: Start DMA engine failed because of invalid state, e.g. the channel is controlled by ETM, so can't start it manually
269  *      - ESP_FAIL: Start DMA engine failed because of other error
270  */
271 esp_err_t gdma_start(gdma_channel_handle_t dma_chan, intptr_t desc_base_addr);
272 
273 /**
274  * @brief Stop DMA engine
275  *
276  * @note This function is allowed to run within ISR context
277  * @note This function is also allowed to run when Cache is disabled, if `CONFIG_GDMA_CTRL_FUNC_IN_IRAM` is enabled
278  *
279  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
280  * @return
281  *      - ESP_OK: Stop DMA engine successfully
282  *      - ESP_ERR_INVALID_ARG: Stop DMA engine failed because of invalid argument
283  *      - ESP_ERR_INVALID_STATE: Stop DMA engine failed because of invalid state, e.g. the channel is controlled by ETM, so can't stop it manually
284  *      - ESP_FAIL: Stop DMA engine failed because of other error
285  */
286 esp_err_t gdma_stop(gdma_channel_handle_t dma_chan);
287 
288 /**
289  * @brief Make the appended descriptors be aware to the DMA engine
290  *
291  * @note This function is allowed to run within ISR context
292  * @note This function is also allowed to run when Cache is disabled, if `CONFIG_GDMA_CTRL_FUNC_IN_IRAM` is enabled
293  * @note This API could also resume a paused DMA engine, make sure new descriptors have been appended to the descriptor chain before calling it.
294  *
295  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
296  * @return
297  *      - ESP_OK: Send append command to DMA engine successfully
298  *      - ESP_ERR_INVALID_ARG: Send append command to DMA engine failed because of invalid argument
299  *      - ESP_FAIL: Send append command to DMA engine failed because of other error
300  */
301 esp_err_t gdma_append(gdma_channel_handle_t dma_chan);
302 
303 /**
304  * @brief Reset DMA channel FIFO and internal finite state machine
305  *
306  * @note This function is allowed to run within ISR context
307  * @note This function is also allowed to run when Cache is disabled, if `CONFIG_GDMA_CTRL_FUNC_IN_IRAM` is enabled
308  * @note Resetting a DMA channel won't break the connection with the target peripheral
309  *
310  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
311  * @return
312  *      - ESP_OK: DMA channel reset successfully
313  *      - ESP_ERR_INVALID_ARG: DMA channel reset failed due to invalid arguments
314  *      - ESP_FAIL: DMA channel reset failed due to other errors
315  */
316 esp_err_t gdma_reset(gdma_channel_handle_t dma_chan);
317 
318 /**
319  * @brief GDMA ETM event configuration
320  */
321 typedef struct {
322     gdma_etm_event_type_t event_type; /*!< GDMA ETM event type */
323 } gdma_etm_event_config_t;
324 
325 /**
326  * @brief Get the ETM event for GDMA channel
327  *
328  * @note The created ETM event object can be deleted later by calling `esp_etm_del_event`
329  *
330  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
331  * @param[in] config GDMA ETM event configuration
332  * @param[out] out_event Returned ETM event handle
333  * @return
334  *      - ESP_OK: Get ETM event successfully
335  *      - ESP_ERR_INVALID_ARG: Get ETM event failed because of invalid argument
336  *      - ESP_ERR_NOT_SUPPORTED: Get ETM event failed because the GDMA hardware doesn't support ETM event
337  *      - ESP_FAIL: Get ETM event failed because of other error
338  */
339 esp_err_t gdma_new_etm_event(gdma_channel_handle_t dma_chan, const gdma_etm_event_config_t *config, esp_etm_event_handle_t *out_event);
340 
341 /**
342  * @brief GDMA ETM task configuration
343  */
344 typedef struct {
345     gdma_etm_task_type_t task_type; /*!< GDMA ETM task type */
346 } gdma_etm_task_config_t;
347 
348 /**
349  * @brief Get the ETM task for GDMA channel
350  *
351  * @note The created ETM task object can be deleted later by calling `esp_etm_del_task`
352  * @note If the GDMA task (e.g. start/stop) is controlled by ETM, then you can't use `gdma_start`/`gdma_stop` to control it.
353  *
354  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
355  * @param[in] config GDMA ETM task configuration
356  * @param[out] out_task Returned ETM task handle
357  * @return
358  *      - ESP_OK: Get ETM task successfully
359  *      - ESP_ERR_INVALID_ARG: Get ETM task failed because of invalid argument
360  *      - ESP_ERR_NOT_SUPPORTED: Get ETM task failed because the gdma hardware doesn't support ETM task
361  *      - ESP_FAIL: Get ETM task failed because of other error
362  */
363 esp_err_t gdma_new_etm_task(gdma_channel_handle_t dma_chan, const gdma_etm_task_config_t *config, esp_etm_task_handle_t *out_task);
364 
365 /**
366  * @brief Get the mask of free M2M trigger IDs
367  *
368  * @note On some ESP targets (e.g. ESP32C3/S3), DMA trigger used for memory copy can be any of valid peripheral's trigger ID,
369  *       which can bring conflict if the peripheral is also using the same trigger ID. This function can return the free IDs
370  *       for memory copy, at the runtime.
371  *
372  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
373  * @param[out] mask Returned mask of free M2M trigger IDs
374  * @return
375  *      - ESP_OK: Get free M2M trigger IDs successfully
376  *      - ESP_ERR_INVALID_ARG: Get free M2M trigger IDs failed because of invalid argument
377  *      - ESP_FAIL: Get free M2M trigger IDs failed because of other error
378  */
379 esp_err_t gdma_get_free_m2m_trig_id_mask(gdma_channel_handle_t dma_chan, uint32_t *mask);
380 
381 #ifdef __cplusplus
382 }
383 #endif
384