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