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-2021 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 #include "cyhal_dma.h"
31 #include "cyhal_system.h"
32 #include "cyhal_hwmgr.h"
33 
34 #if (CYHAL_DRIVER_AVAILABLE_DMA)
35 
36 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
37 #include "cyhal_dma_dmac.h"
38 #endif
39 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
40 #include "cyhal_dma_dw.h"
41 #endif
42 
43 #if defined(__cplusplus)
44 extern "C" {
45 #endif /* __cplusplus */
46 
47 
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)48 cy_rslt_t cyhal_dma_init_adv(
49     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)
50 {
51     CY_ASSERT(NULL != obj);
52 
53     obj->direction = direction;
54     obj->callback_data.callback = NULL;
55     obj->callback_data.callback_arg = NULL;
56     obj->irq_cause = 0;
57     obj->source = CYHAL_TRIGGER_CPUSS_ZERO;
58     obj->owned_by_configurator = false;
59 
60     cy_rslt_t rslt;
61     cyhal_source_t *src_trigger = (NULL == src) ? NULL : &src->source;
62     cyhal_dest_t *dest_trigger = (NULL == dest) ? NULL : &dest->dest;
63 
64 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW && !_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
65     /* Only DW available. */
66     rslt = _cyhal_dma_dw_init(obj, src_trigger, dest_trigger, priority);
67 #elif (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC && !_CYHAL_DRIVER_AVAILABLE_DMA_DW)
68     /* Only DMAC available. */
69     rslt = _cyhal_dma_dmac_init(obj, src_trigger, dest_trigger, priority);
70 #else
71     /* DMAC is designed with high memory bandwidth for memory to memory
72      * transfers so prefer it when direction is MEM2MEM. Otherwise prefer
73      * Datawire as it is designed for low latency memory to peripheral or
74      * peripheral to memory transfers. Note: Both DMA types can handle any
75      * direction value so using a non-ideal DMA type is ok.*/
76     if(direction == CYHAL_DMA_DIRECTION_MEM2MEM)
77     {
78         rslt = _cyhal_dma_dmac_init(obj, src_trigger, dest_trigger, priority);
79         /* If no DMAC channels are available fall back on DW. */
80         if(CYHAL_HWMGR_RSLT_ERR_NONE_FREE == rslt)
81             rslt = _cyhal_dma_dw_init(obj, src_trigger, dest_trigger, priority);
82     }
83     else
84     {
85         rslt = _cyhal_dma_dw_init(obj, src_trigger, dest_trigger, priority);
86         /* If no DW channels are available fall back on DMAC. */
87         if(CYHAL_HWMGR_RSLT_ERR_NONE_FREE == rslt)
88             rslt = _cyhal_dma_dmac_init(obj, src_trigger, dest_trigger, priority);
89     }
90 #endif
91 
92     if (CY_RSLT_SUCCESS == rslt)
93     {
94         if (NULL != src)
95         {
96             rslt = cyhal_dma_connect_digital(obj, src->source, src->input);
97             obj->source = src->source;
98         }
99 
100         if ((CY_RSLT_SUCCESS == rslt) && (NULL != dest))
101         {
102             rslt = cyhal_dma_enable_output(obj, dest->output, dest_source);
103         }
104 
105         // If connection setup failed, free the resources.
106         if (CY_RSLT_SUCCESS != rslt)
107         {
108             cyhal_dma_free(obj);
109         }
110     }
111 
112     return rslt;
113 }
114 
cyhal_dma_init_cfg(cyhal_dma_t * obj,const cyhal_dma_configurator_t * cfg)115 cy_rslt_t cyhal_dma_init_cfg(cyhal_dma_t *obj, const cyhal_dma_configurator_t *cfg)
116 {
117     CY_ASSERT(NULL != obj);
118     CY_ASSERT(NULL != cfg);
119 
120     obj->owned_by_configurator = true;
121 
122 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
123     #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
124     if (cfg->resource->type == CYHAL_RSC_DMA)
125     #endif
126     {
127         return _cyhal_dma_dmac_init_cfg(obj, cfg);
128     }
129 #endif
130 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
131     CY_ASSERT(cfg->resource->type == CYHAL_RSC_DW);
132     return _cyhal_dma_dw_init_cfg(obj, cfg);
133 #endif
134 }
135 
cyhal_dma_free(cyhal_dma_t * obj)136 void cyhal_dma_free(cyhal_dma_t *obj)
137 {
138     CY_ASSERT(NULL != obj);
139     CY_ASSERT(!cyhal_dma_is_busy(obj));
140 
141     cy_rslt_t rslt;
142     // DMA signal enum values don't matter since they are actually the same connection
143     rslt = cyhal_dma_disable_output(obj, CYHAL_DMA_OUTPUT_TRIGGER_ALL_ELEMENTS);
144     CY_ASSERT(CY_RSLT_SUCCESS == rslt);
145     if (CYHAL_TRIGGER_CPUSS_ZERO != obj->source)
146     {
147         rslt = cyhal_dma_disconnect_digital(obj, obj->source, CYHAL_DMA_INPUT_TRIGGER_ALL_ELEMENTS);
148         CY_ASSERT(CY_RSLT_SUCCESS == rslt);
149     }
150     (void)rslt; // Disable compiler warning in release build
151 
152 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
153     #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
154     if(obj->resource.type == CYHAL_RSC_DMA)
155     #endif
156     {
157         _cyhal_dma_dmac_free(obj);
158     }
159 #endif
160 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
161     #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
162     if(obj->resource.type == CYHAL_RSC_DW)
163     #endif
164     {
165         _cyhal_dma_dw_free(obj);
166     }
167 #endif
168 
169     if (!obj->owned_by_configurator)
170     {
171         cyhal_hwmgr_free(&obj->resource);
172     }
173 }
174 
cyhal_dma_configure(cyhal_dma_t * obj,const cyhal_dma_cfg_t * cfg)175 cy_rslt_t cyhal_dma_configure(cyhal_dma_t *obj, const cyhal_dma_cfg_t *cfg)
176 {
177     CY_ASSERT(NULL != obj);
178 
179 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
180     #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
181     if(obj->resource.type == CYHAL_RSC_DMA)
182     #endif
183     {
184         return _cyhal_dma_dmac_configure(obj, cfg);
185     }
186 #endif
187 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
188     CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
189     return _cyhal_dma_dw_configure(obj, cfg);
190 #endif
191 }
192 
cyhal_dma_start_transfer(cyhal_dma_t * obj)193 cy_rslt_t cyhal_dma_start_transfer(cyhal_dma_t *obj)
194 {
195     CY_ASSERT(NULL != obj);
196 
197 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
198     #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
199     if(obj->resource.type == CYHAL_RSC_DMA)
200     #endif
201     {
202         return _cyhal_dma_dmac_start_transfer(obj);
203     }
204 #endif
205 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
206     CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
207     return _cyhal_dma_dw_start_transfer(obj);
208 #endif
209 }
210 
cyhal_dma_enable(cyhal_dma_t * obj)211 cy_rslt_t cyhal_dma_enable(cyhal_dma_t *obj)
212 {
213     CY_ASSERT(NULL != obj);
214 
215 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
216     #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
217     if(obj->resource.type == CYHAL_RSC_DMA)
218     #endif
219     {
220         return _cyhal_dma_dmac_enable(obj);
221     }
222 #endif
223 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
224     CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
225     return _cyhal_dma_dw_enable(obj);
226 #endif
227 }
228 
cyhal_dma_disable(cyhal_dma_t * obj)229 cy_rslt_t cyhal_dma_disable(cyhal_dma_t *obj)
230 {
231     CY_ASSERT(NULL != obj);
232 
233 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
234     #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
235     if(obj->resource.type == CYHAL_RSC_DMA)
236     #endif
237     {
238         return _cyhal_dma_dmac_disable(obj);
239     }
240 #endif
241 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
242     CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
243     return _cyhal_dma_dw_disable(obj);
244 #endif
245 }
246 
cyhal_dma_is_busy(cyhal_dma_t * obj)247 bool cyhal_dma_is_busy(cyhal_dma_t *obj)
248 {
249     CY_ASSERT(NULL != obj);
250 
251 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
252     #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
253     if(obj->resource.type == CYHAL_RSC_DMA)
254     #endif
255     {
256         return _cyhal_dma_dmac_is_busy(obj);
257     }
258 #endif
259 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
260     CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
261     return _cyhal_dma_dw_is_busy(obj);
262 #endif
263 }
264 
cyhal_dma_register_callback(cyhal_dma_t * obj,cyhal_dma_event_callback_t callback,void * callback_arg)265 void cyhal_dma_register_callback(cyhal_dma_t *obj, cyhal_dma_event_callback_t callback, void *callback_arg)
266 {
267     CY_ASSERT(NULL != obj);
268 
269     uint32_t saved_intr_status = cyhal_system_critical_section_enter();
270     obj->callback_data.callback = (cy_israddress)callback;
271     obj->callback_data.callback_arg = callback_arg;
272     cyhal_system_critical_section_exit(saved_intr_status);
273 }
274 
cyhal_dma_enable_event(cyhal_dma_t * obj,cyhal_dma_event_t event,uint8_t intr_priority,bool enable)275 void cyhal_dma_enable_event(cyhal_dma_t *obj, cyhal_dma_event_t event, uint8_t intr_priority, bool enable)
276 {
277     CY_ASSERT(NULL != obj);
278 
279 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
280     #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
281     if(obj->resource.type == CYHAL_RSC_DMA)
282     #endif
283     {
284         _cyhal_dma_dmac_enable_event(obj, event, intr_priority, enable);
285         return;
286     }
287 #endif
288 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
289     CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
290     _cyhal_dma_dw_enable_event(obj, event, intr_priority, enable);
291 #endif
292 }
293 
cyhal_dma_connect_digital(cyhal_dma_t * obj,cyhal_source_t source,cyhal_dma_input_t input)294 cy_rslt_t cyhal_dma_connect_digital(cyhal_dma_t *obj, cyhal_source_t source, cyhal_dma_input_t input)
295 {
296     CY_ASSERT(NULL != obj);
297 
298 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
299     #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
300     if(obj->resource.type == CYHAL_RSC_DMA)
301     #endif
302     {
303         return _cyhal_dma_dmac_connect_digital(obj, source, input);
304     }
305 #endif
306 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
307     CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
308     return _cyhal_dma_dw_connect_digital(obj, source, input);
309 #endif
310 }
311 
cyhal_dma_enable_output(cyhal_dma_t * obj,cyhal_dma_output_t output,cyhal_source_t * source)312 cy_rslt_t cyhal_dma_enable_output(cyhal_dma_t *obj, cyhal_dma_output_t output, cyhal_source_t *source)
313 {
314     CY_ASSERT(NULL != obj);
315 
316 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
317     #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
318     if(obj->resource.type == CYHAL_RSC_DMA)
319     #endif
320     {
321         return _cyhal_dma_dmac_enable_output(obj, output, source);
322     }
323 #endif
324 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
325     CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
326     return _cyhal_dma_dw_enable_output(obj, output, source);
327 #endif
328 }
329 
cyhal_dma_disconnect_digital(cyhal_dma_t * obj,cyhal_source_t source,cyhal_dma_input_t input)330 cy_rslt_t cyhal_dma_disconnect_digital(cyhal_dma_t *obj, cyhal_source_t source, cyhal_dma_input_t input)
331 {
332     CY_ASSERT(NULL != obj);
333 
334 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
335     #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
336     if(obj->resource.type == CYHAL_RSC_DMA)
337     #endif
338     {
339         return _cyhal_dma_dmac_disconnect_digital(obj, source, input);
340     }
341 #endif
342 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
343     CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
344     return _cyhal_dma_dw_disconnect_digital(obj, source, input);
345 #endif
346 }
347 
cyhal_dma_disable_output(cyhal_dma_t * obj,cyhal_dma_output_t output)348 cy_rslt_t cyhal_dma_disable_output(cyhal_dma_t *obj, cyhal_dma_output_t output)
349 {
350     CY_ASSERT(NULL != obj);
351 
352 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
353     #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
354     if(obj->resource.type == CYHAL_RSC_DMA)
355     #endif
356     {
357         return _cyhal_dma_dmac_disable_output(obj, output);
358     }
359 #endif
360 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
361     CY_ASSERT(obj->resource.type == CYHAL_RSC_DW);
362     return _cyhal_dma_dw_disable_output(obj, output);
363 #endif
364 }
365 
366 #if defined(__cplusplus)
367 }
368 #endif /* __cplusplus */
369 
370 #endif /* (CYHAL_DRIVER_AVAILABLE_DMA) */
371