1 /***************************************************************************//**
2 * \file cyhal_dma.c
3 *
4 * \brief
5 * Implements a high level interface for interacting with the Infineon DMA.
6 * This implementation abstracts out the chip specific details. If any chip specific
7 * functionality is necessary, or performance is critical the low level functions
8 * can be used directly.
9 *
10 ********************************************************************************
11 * \copyright
12 * Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
13 * an affiliate of Cypress Semiconductor Corporation
14 *
15 * SPDX-License-Identifier: Apache-2.0
16 *
17 * Licensed under the Apache License, Version 2.0 (the "License");
18 * you may not use this file except in compliance with the License.
19 * You may obtain a copy of the License at
20 *
21 * http://www.apache.org/licenses/LICENSE-2.0
22 *
23 * Unless required by applicable law or agreed to in writing, software
24 * distributed under the License is distributed on an "AS IS" BASIS,
25 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26 * See the License for the specific language governing permissions and
27 * limitations under the License.
28 *******************************************************************************/
29
30 /**
31 * \addtogroup group_hal_impl_dma DMA (Direct Memory Access)
32 * \ingroup group_hal_impl
33 * \{
34 * \section section_hal_impl_dma_data_arr_requirement User-provided data arrays requirements
35 * CM7 cores in CAT1C devices support Data Cache. Data Cache line is 32 bytes. User needs to make sure that
36 * the source and destination buffer pointers points to 32 byte aligned data. User can use CY_ALIGN(32) macro for
37 * 32 byte alignment.
38 *
39 * \} group_hal_impl_dma
40 */
41
42 #include "cyhal_dma.h"
43 #include "cyhal_system.h"
44 #include "cyhal_hwmgr.h"
45 #include <string.h>
46
47 #if (CYHAL_DRIVER_AVAILABLE_DMA)
48
49 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
50 #include "cyhal_dma_dmac.h"
51 #endif
52 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
53 #include "cyhal_dma_dw.h"
54 #endif
55
56 #if defined(__cplusplus)
57 extern "C" {
58 #endif /* __cplusplus */
59
60
cyhal_dma_init_adv(cyhal_dma_t * obj,cyhal_dma_src_t * src,cyhal_dma_dest_t * dest,cyhal_source_t * dest_source,uint8_t priority,cyhal_dma_direction_t direction)61 cy_rslt_t cyhal_dma_init_adv(
62 cyhal_dma_t *obj, cyhal_dma_src_t *src, cyhal_dma_dest_t *dest, cyhal_source_t *dest_source, uint8_t priority, cyhal_dma_direction_t direction)
63 {
64 CY_ASSERT(NULL != obj);
65
66 memset(obj, 0u, sizeof(*obj));
67
68 obj->direction = direction;
69 obj->callback_data.callback = NULL;
70 obj->callback_data.callback_arg = NULL;
71 obj->irq_cause = 0;
72 obj->source = CYHAL_TRIGGER_CPUSS_ZERO;
73 obj->owned_by_configurator = false;
74
75 cy_rslt_t rslt;
76 cyhal_source_t *src_trigger = (NULL == src) ? NULL : &src->source;
77 cyhal_dest_t *dest_trigger = (NULL == dest) ? NULL : &dest->dest;
78
79 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW && !_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
80 /* Only DW available. */
81 rslt = _cyhal_dma_dw_init(obj, src_trigger, dest_trigger, priority);
82 #elif (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC && !_CYHAL_DRIVER_AVAILABLE_DMA_DW)
83 /* Only DMAC available. */
84 rslt = _cyhal_dma_dmac_init(obj, src_trigger, dest_trigger, priority);
85 #else
86 /* DMAC is designed with high memory bandwidth for memory to memory
87 * transfers so prefer it when direction is MEM2MEM. Otherwise prefer
88 * Datawire as it is designed for low latency memory to peripheral or
89 * peripheral to memory transfers. Note: Both DMA types can handle any
90 * direction value so using a non-ideal DMA type is ok.*/
91 if(direction == CYHAL_DMA_DIRECTION_MEM2MEM)
92 {
93 rslt = _cyhal_dma_dmac_init(obj, src_trigger, dest_trigger, priority);
94 /* If no DMAC channels are available fall back on DW. */
95 if(CYHAL_HWMGR_RSLT_ERR_NONE_FREE == rslt)
96 rslt = _cyhal_dma_dw_init(obj, src_trigger, dest_trigger, priority);
97 }
98 else
99 {
100 rslt = _cyhal_dma_dw_init(obj, src_trigger, dest_trigger, priority);
101 /* If no DW channels are available fall back on DMAC. */
102 if(CYHAL_HWMGR_RSLT_ERR_NONE_FREE == rslt)
103 rslt = _cyhal_dma_dmac_init(obj, src_trigger, dest_trigger, priority);
104 }
105 #endif
106
107 if (CY_RSLT_SUCCESS == rslt)
108 {
109 if (NULL != src)
110 {
111 rslt = cyhal_dma_connect_digital(obj, src->source, src->input);
112 obj->source = src->source;
113 }
114
115 if ((CY_RSLT_SUCCESS == rslt) && (NULL != dest))
116 {
117 rslt = cyhal_dma_enable_output(obj, dest->output, dest_source);
118 }
119
120 // If connection setup failed, free the resources.
121 if (CY_RSLT_SUCCESS != rslt)
122 {
123 cyhal_dma_free(obj);
124 }
125 }
126
127 return rslt;
128 }
129
cyhal_dma_init_cfg(cyhal_dma_t * obj,const cyhal_dma_configurator_t * cfg)130 cy_rslt_t cyhal_dma_init_cfg(cyhal_dma_t *obj, const cyhal_dma_configurator_t *cfg)
131 {
132 CY_ASSERT(NULL != obj);
133 CY_ASSERT(NULL != cfg);
134
135 memset(obj, 0u, sizeof(*obj));
136
137 obj->owned_by_configurator = true;
138
139 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
140 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
141 if (cfg->resource->type == CYHAL_RSC_DMA)
142 #endif
143 {
144 return _cyhal_dma_dmac_init_cfg(obj, cfg);
145 }
146 #endif
147 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
148 CY_ASSERT(cfg->resource->type == CYHAL_RSC_DW);
149 return _cyhal_dma_dw_init_cfg(obj, cfg);
150 #endif
151 }
152
cyhal_dma_free(cyhal_dma_t * obj)153 void cyhal_dma_free(cyhal_dma_t *obj)
154 {
155 CY_ASSERT(NULL != obj);
156 CY_ASSERT(!cyhal_dma_is_busy(obj));
157
158 cy_rslt_t rslt;
159 // DMA signal enum values don't matter since they are actually the same connection
160 rslt = cyhal_dma_disable_output(obj, CYHAL_DMA_OUTPUT_TRIGGER_ALL_ELEMENTS);
161 CY_ASSERT(CY_RSLT_SUCCESS == rslt);
162 if (CYHAL_TRIGGER_CPUSS_ZERO != obj->source)
163 {
164 rslt = cyhal_dma_disconnect_digital(obj, obj->source, CYHAL_DMA_INPUT_TRIGGER_ALL_ELEMENTS);
165 CY_ASSERT(CY_RSLT_SUCCESS == rslt);
166 }
167 (void)rslt; // Disable compiler warning in release build
168
169 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
170 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
171 if(obj->resource.type == CYHAL_RSC_DMA)
172 #endif
173 {
174 _cyhal_dma_dmac_free(obj);
175 }
176 #endif
177 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
178 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
179 if(obj->resource.type == CYHAL_RSC_DW)
180 #endif
181 {
182 _cyhal_dma_dw_free(obj);
183 }
184 #endif
185
186 if (!obj->owned_by_configurator)
187 {
188 cyhal_hwmgr_free(&obj->resource);
189 }
190 }
191
cyhal_dma_configure(cyhal_dma_t * obj,const cyhal_dma_cfg_t * cfg)192 cy_rslt_t cyhal_dma_configure(cyhal_dma_t *obj, const cyhal_dma_cfg_t *cfg)
193 {
194 CY_ASSERT(NULL != obj);
195
196 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
197 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
198 if(obj->resource.type == CYHAL_RSC_DMA)
199 #endif
200 {
201 return _cyhal_dma_dmac_configure(obj, cfg);
202 }
203 #endif
204 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
205 CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
206 return _cyhal_dma_dw_configure(obj, cfg);
207 #endif
208 }
209
cyhal_dma_start_transfer(cyhal_dma_t * obj)210 cy_rslt_t cyhal_dma_start_transfer(cyhal_dma_t *obj)
211 {
212 CY_ASSERT(NULL != obj);
213
214 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
215 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
216 if(obj->resource.type == CYHAL_RSC_DMA)
217 #endif
218 {
219 return _cyhal_dma_dmac_start_transfer(obj);
220 }
221 #endif
222 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
223 CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
224 return _cyhal_dma_dw_start_transfer(obj);
225 #endif
226 }
227
cyhal_dma_enable(cyhal_dma_t * obj)228 cy_rslt_t cyhal_dma_enable(cyhal_dma_t *obj)
229 {
230 CY_ASSERT(NULL != obj);
231
232 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
233 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
234 if(obj->resource.type == CYHAL_RSC_DMA)
235 #endif
236 {
237 return _cyhal_dma_dmac_enable(obj);
238 }
239 #endif
240 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
241 CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
242 return _cyhal_dma_dw_enable(obj);
243 #endif
244 }
245
cyhal_dma_disable(cyhal_dma_t * obj)246 cy_rslt_t cyhal_dma_disable(cyhal_dma_t *obj)
247 {
248 CY_ASSERT(NULL != obj);
249
250 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
251 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
252 if(obj->resource.type == CYHAL_RSC_DMA)
253 #endif
254 {
255 return _cyhal_dma_dmac_disable(obj);
256 }
257 #endif
258 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
259 CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
260 return _cyhal_dma_dw_disable(obj);
261 #endif
262 }
263
cyhal_dma_is_busy(cyhal_dma_t * obj)264 bool cyhal_dma_is_busy(cyhal_dma_t *obj)
265 {
266 CY_ASSERT(NULL != obj);
267
268 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
269 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
270 if(obj->resource.type == CYHAL_RSC_DMA)
271 #endif
272 {
273 return _cyhal_dma_dmac_is_busy(obj);
274 }
275 #endif
276 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
277 CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
278 return _cyhal_dma_dw_is_busy(obj);
279 #endif
280 }
281
cyhal_dma_register_callback(cyhal_dma_t * obj,cyhal_dma_event_callback_t callback,void * callback_arg)282 void cyhal_dma_register_callback(cyhal_dma_t *obj, cyhal_dma_event_callback_t callback, void *callback_arg)
283 {
284 CY_ASSERT(NULL != obj);
285
286 uint32_t saved_intr_status = cyhal_system_critical_section_enter();
287 obj->callback_data.callback = (cy_israddress)callback;
288 obj->callback_data.callback_arg = callback_arg;
289 cyhal_system_critical_section_exit(saved_intr_status);
290 }
291
cyhal_dma_enable_event(cyhal_dma_t * obj,cyhal_dma_event_t event,uint8_t intr_priority,bool enable)292 void cyhal_dma_enable_event(cyhal_dma_t *obj, cyhal_dma_event_t event, uint8_t intr_priority, bool enable)
293 {
294 CY_ASSERT(NULL != obj);
295
296 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
297 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
298 if(obj->resource.type == CYHAL_RSC_DMA)
299 #endif
300 {
301 _cyhal_dma_dmac_enable_event(obj, event, intr_priority, enable);
302 return;
303 }
304 #endif
305 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
306 CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
307 _cyhal_dma_dw_enable_event(obj, event, intr_priority, enable);
308 #endif
309 }
310
cyhal_dma_connect_digital(cyhal_dma_t * obj,cyhal_source_t source,cyhal_dma_input_t input)311 cy_rslt_t cyhal_dma_connect_digital(cyhal_dma_t *obj, cyhal_source_t source, cyhal_dma_input_t input)
312 {
313 CY_ASSERT(NULL != obj);
314
315 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
316 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
317 if(obj->resource.type == CYHAL_RSC_DMA)
318 #endif
319 {
320 return _cyhal_dma_dmac_connect_digital(obj, source, input);
321 }
322 #endif
323 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
324 CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
325 return _cyhal_dma_dw_connect_digital(obj, source, input);
326 #endif
327 }
328
cyhal_dma_enable_output(cyhal_dma_t * obj,cyhal_dma_output_t output,cyhal_source_t * source)329 cy_rslt_t cyhal_dma_enable_output(cyhal_dma_t *obj, cyhal_dma_output_t output, cyhal_source_t *source)
330 {
331 CY_ASSERT(NULL != obj);
332
333 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
334 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
335 if(obj->resource.type == CYHAL_RSC_DMA)
336 #endif
337 {
338 return _cyhal_dma_dmac_enable_output(obj, output, source);
339 }
340 #endif
341 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
342 CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
343 return _cyhal_dma_dw_enable_output(obj, output, source);
344 #endif
345 }
346
cyhal_dma_disconnect_digital(cyhal_dma_t * obj,cyhal_source_t source,cyhal_dma_input_t input)347 cy_rslt_t cyhal_dma_disconnect_digital(cyhal_dma_t *obj, cyhal_source_t source, cyhal_dma_input_t input)
348 {
349 CY_ASSERT(NULL != obj);
350
351 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
352 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
353 if(obj->resource.type == CYHAL_RSC_DMA)
354 #endif
355 {
356 return _cyhal_dma_dmac_disconnect_digital(obj, source, input);
357 }
358 #endif
359 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
360 CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
361 return _cyhal_dma_dw_disconnect_digital(obj, source, input);
362 #endif
363 }
364
cyhal_dma_disable_output(cyhal_dma_t * obj,cyhal_dma_output_t output)365 cy_rslt_t cyhal_dma_disable_output(cyhal_dma_t *obj, cyhal_dma_output_t output)
366 {
367 CY_ASSERT(NULL != obj);
368
369 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
370 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
371 if(obj->resource.type == CYHAL_RSC_DMA)
372 #endif
373 {
374 return _cyhal_dma_dmac_disable_output(obj, output);
375 }
376 #endif
377 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
378 CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
379 return _cyhal_dma_dw_disable_output(obj, output);
380 #endif
381 }
382
383 #if defined(__cplusplus)
384 }
385 #endif /* __cplusplus */
386
387 #endif /* (CYHAL_DRIVER_AVAILABLE_DMA) */
388