1 /***************************************************************************//**
2 * \file cyhal_dma_dmac.c
3 *
4 * \brief
5 * Implements a high level interface for interacting with the Infineon DMAC.
6 *
7 ********************************************************************************
8 * \copyright
9 * Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
10 * an affiliate of Cypress Semiconductor Corporation
11 *
12 * SPDX-License-Identifier: Apache-2.0
13 *
14 * Licensed under the Apache License, Version 2.0 (the "License");
15 * you may not use this file except in compliance with the License.
16 * You may obtain a copy of the License at
17 *
18 * http://www.apache.org/licenses/LICENSE-2.0
19 *
20 * Unless required by applicable law or agreed to in writing, software
21 * distributed under the License is distributed on an "AS IS" BASIS,
22 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 * See the License for the specific language governing permissions and
24 * limitations under the License.
25 *******************************************************************************/
26
27 #include "cyhal_dma.h"
28 #include "cyhal_dma_dmac.h"
29 #include "cyhal_dma_impl.h"
30 #include "cyhal_hwmgr_impl.h"
31 #include "cyhal_interconnect.h"
32 #include "cyhal_syspm.h"
33 #include "cyhal_utils.h"
34 #include "cyhal_irq_impl.h"
35 #include "cyhal_triggers.h"
36
37 #if defined(__cplusplus)
38 extern "C" {
39 #endif
40
41 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC)
42
43 #if defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC)
44 #define DMAC0_IRQn (cpuss_interrupts_dmac_0_IRQn)
45 #define GET_RESOURCE_DATA(x) (x.dmac)
46 typedef DMAC_Type cyhal_dmac_hw_type;
47 #elif defined(CY_IP_M0S8CPUSSV3_DMAC)
48 #define DMAC0_IRQn (cpuss_interrupt_dma_IRQn)
49 #define GET_RESOURCE_DATA(x) (x)
50 #define CY_TRIGGER_TWO_CYCLES (2)
51 typedef DMAC_Type cyhal_dmac_hw_type;
52 #elif defined(CY_IP_MXAHBDMAC)
53 #define DMAC0_IRQn (cpuss_interrupts_dmac0_0_IRQn)
54 #define DMAC1_IRQn (cpuss_interrupts_dmac1_0_IRQn)
55 #define GET_RESOURCE_DATA(x) (x.dmac)
56 typedef MXAHBDMAC_Type cyhal_dmac_hw_type;
57 #elif defined(CY_IP_MXSAXIDMAC)
58 #define DMAC0_IRQn (m55appcpuss_interrupts_axidmac_0_IRQn)
59 typedef SAXI_DMAC_Type cyhal_dmac_hw_type;
60 #define GET_RESOURCE_DATA(x) (x.dmac)
61 #endif
62
63 #if defined (AXI_DMAC_CH_NR)
64 #if APPCPUSS_AXIDMAC1_PRESENT
65 #define NUM_DMAC0_CHANNELS (APPCPUSS_AXIDMAC0_CH_NR)
66 #define NUM_DMAC_CHANNELS (APPCPUSS_AXIDMAC0_CH_NR + APPCPUSS_AXIDMAC1_CH_NR)
67 #else
68 #define NUM_DMAC0_CHANNELS (APPCPUSS_AXIDMAC0_CH_NR)
69 #define NUM_DMAC_CHANNELS (APPCPUSS_AXIDMAC0_CH_NR)
70 #endif
71 #elif defined(CPUSS_DMAC0_CH_NR) && defined(CPUSS_DMAC1_CH_NR) && (CPUSS_DMAC0_PRESENT > 0) && (CPUSS_DMAC1_PRESENT > 0)
72 #define NUM_DMAC_CHANNELS (CPUSS_DMAC0_CH_NR + CPUSS_DMAC1_CH_NR)
73 #define NUM_DMAC0_CHANNELS (CPUSS_DMAC0_CH_NR)
74 #elif defined(CPUSS_DMAC_CH_NR)
75 #define NUM_DMAC_CHANNELS (CPUSS_DMAC_CH_NR)
76 #define CPUSS_DMAC0_CH_NR (CPUSS_DMAC_CH_NR)
77 #define NUM_DMAC0_CHANNELS (CPUSS_DMAC_CH_NR)
78 #define CYHAL_TRIGGER_CPUSS_DMAC0_TR_IN0 (CYHAL_TRIGGER_CPUSS_DMAC_TR_IN0)
79 #define _CYHAL_TRIGGER_CPUSS_DMAC0_TR_OUT0 (_CYHAL_TRIGGER_CPUSS_DMAC_TR_OUT0)
80 #endif
81
82 //Wrappers for PDL functions and Macros for AXI DMAC differences
83 #if defined(CY_IP_MXSAXIDMAC)
84 #define _cyhal_dmac_channel_enable(base, channel) Cy_AXIDMAC_Channel_Enable(base, channel)
85 #define _cyhal_dmac_channel_disable(base, channel) Cy_AXIDMAC_Channel_Disable(base, channel)
86 #define _cyhal_dmac_get_active_channel(base) Cy_AXIDMAC_GetActiveChannel(base)
87 #define _cyhal_dmac_channel_get_interrupt_status_masked(base, channel) Cy_AXIDMAC_Channel_GetInterruptStatusMasked(base, channel)
88 #define _CYHAL_DMAC_CHANNEL_ENABLED (CY_AXIDMAC_CHANNEL_ENABLED)
89 #define _CYHAL_DMAC_CHANNEL_DISABLED (CY_AXIDMAC_CHANNEL_DISABLED)
90 #define _CYHAL_DMAC_DESCR (CY_AXIDMAC_DESCR)
91 #define _CYHAL_DMAC_M_LOOP (CY_AXIDMAC_M_LOOP)
92 #define _CYHAL_DMAC_CH_DESCR_CTL_TR_IN_TYPE_Msk (SAXI_DMAC_CH_DESCR_CTL_TR_IN_TYPE_Msk)
93 #define _CYHAL_DMAC_CH_DESCR_CTL_TR_OUT_TYPE_Msk (SAXI_DMAC_CH_DESCR_CTL_TR_OUT_TYPE_Msk)
94 #define _CYHAL_DMAC_CH_DESCR_CTL_TR_IN_TYPE (SAXI_DMAC_CH_DESCR_CTL_TR_IN_TYPE)
95 #define _CYHAL_DMAC_CH_DESCR_CTL_TR_OUT_TYPE (SAXI_DMAC_CH_DESCR_CTL_TR_OUT_TYPE)
96 #define _CYHAL_DMAC_CH_DESCR_CTL_TR_IN_TYPE_Pos (SAXI_DMAC_CH_DESCR_CTL_TR_IN_TYPE_Pos)
97 #define _CYHAL_DMAC_CH_DESCR_CTL_TR_OUT_TYPE_Pos (SAXI_DMAC_CH_DESCR_CTL_TR_OUT_TYPE_Pos)
98 #define _CYHAL_TRIGGER_CPUSS_ZERO (CYHAL_TRIGGER_M33SYSCPUSS_ZERO)
99 #elif defined(CY_IP_MXAHBDMAC) || defined(CY_IP_M0S8CPUSSV3_DMAC) || defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC)
100 #define _cyhal_dmac_channel_enable(base, channel) Cy_DMAC_Channel_Enable(base, channel)
101 #define _cyhal_dmac_channel_disable(base, channel) Cy_DMAC_Channel_Disable(base, channel)
102 #define _cyhal_dmac_get_active_channel(base) Cy_DMAC_GetActiveChannel(base)
103 #define _cyhal_dmac_channel_get_interrupt_status_masked(base, channel) Cy_DMAC_Channel_GetInterruptStatusMasked(base, channel)
104 #define _CYHAL_DMAC_CHANNEL_ENABLED (CY_DMAC_CHANNEL_ENABLED)
105 #define _CYHAL_DMAC_CHANNEL_DISABLED (CY_DMAC_CHANNEL_DISABLED)
106 #define _CYHAL_DMAC_DESCR (CY_DMAC_DESCR)
107 #define _CYHAL_DMAC_X_LOOP (CY_DMAC_X_LOOP)
108 #define _CYHAL_DMAC_CH_DESCR_CTL_TR_IN_TYPE_Msk (DMAC_CH_V2_DESCR_CTL_TR_IN_TYPE_Msk)
109 #define _CYHAL_DMAC_CH_DESCR_CTL_TR_OUT_TYPE_Msk (DMAC_CH_V2_DESCR_CTL_TR_OUT_TYPE_Msk)
110 #define _CYHAL_DMAC_CH_DESCR_CTL_TR_IN_TYPE (DMAC_CH_V2_DESCR_CTL_TR_IN_TYPE)
111 #define _CYHAL_DMAC_CH_DESCR_CTL_TR_OUT_TYPE (DMAC_CH_V2_DESCR_CTL_TR_OUT_TYPE)
112 #define _CYHAL_DMAC_CH_DESCR_CTL_TR_IN_TYPE_Pos (DMAC_CH_V2_DESCR_CTL_TR_IN_TYPE_Pos)
113 #define _CYHAL_DMAC_CH_DESCR_CTL_TR_OUT_TYPE_Pos (DMAC_CH_V2_DESCR_CTL_TR_OUT_TYPE_Pos)
114 #define _CYHAL_TRIGGER_CPUSS_ZERO (CYHAL_TRIGGER_CPUSS_ZERO)
115 #endif
116
117
118 static cyhal_dma_t* _cyhal_dma_dmac_config_structs[NUM_DMAC_CHANNELS];
119
120 /** Default dmac descriptor config */
121 #if defined(CY_IP_MXSAXIDMAC)
122 static const cy_stc_axidmac_descriptor_config_t _cyhal_dma_dmac_default_descriptor_config =
123 #else
124 static const cy_stc_dmac_descriptor_config_t _cyhal_dma_dmac_default_descriptor_config =
125 #endif
126 {
127 .srcAddress = 0, // Overriden by cyhal_dma_cfg_t.src_addr
128 .dstAddress = 0, // Overriden by cyhal_dma_cfg_t.dst_addr
129 #if defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC) || defined(CY_IP_MXAHBDMAC)
130 .dataSize = CY_DMAC_WORD, // Overridden by cyhal_dma_cfg_t.transfer_width
131 .dstTransferSize = CY_DMAC_TRANSFER_SIZE_DATA, // Overriden by direction
132 .srcTransferSize = CY_DMAC_TRANSFER_SIZE_DATA, // Overriden by direction
133 .retrigger = CY_DMAC_RETRIG_IM,
134 .interruptType = CY_DMAC_DESCR, // Overridden by cyhal_dma_cfg_t.action
135 .triggerOutType = CY_DMAC_DESCR_CHAIN, // Overridden by [en/dis]able_output()
136 .channelState = CY_DMAC_CHANNEL_ENABLED,
137 .triggerInType = CY_DMAC_DESCR, // Overridden by cyhal_dma_cfg_t.action & [dis]connect_digital()
138 .dataPrefetch = false,
139 .descriptorType = CY_DMAC_1D_TRANSFER, // Overriden by cyhal_dma_cfg_t.burst_size
140 .srcXincrement = 1U, // Overriden by cyhal_dma_cfg_t.src_increment
141 .dstXincrement = 1U, // Overriden by cyhal_dma_cfg_t.dst_increment
142 .xCount = 1UL, // Overriden by cyhal_dma_cfg_t.length/burst_size
143 .srcYincrement = 1U, // Overriden by cyhal_dma_cfg_t.burst_size
144 .dstYincrement = 1U, // Overriden by cyhal_dma_cfg_t.burst_size
145 .yCount = 1UL, // Overriden by cyhal_dma_cfg_t.length
146 .nextDescriptor = 0,
147 #elif defined(CY_IP_M0S8CPUSSV3_DMAC)
148 .dataSize = CY_DMAC_WORD, // Overridden by cyhal_dma_cfg_t.transfer_width
149 .dstTransferSize = CY_DMAC_TRANSFER_SIZE_DATA, // Overriden by direction
150 .srcTransferSize = CY_DMAC_TRANSFER_SIZE_DATA, // Overriden by direction
151 .retrigger = CY_DMAC_RETRIG_IM,
152 .triggerType = CY_DMAC_SINGLE_DESCR,
153 .dataCount = 1, // Overriden by cyhal_dma_cfg_t.length
154 .dstAddrIncrement = true, // Overriden by cyhal_dma_cfg_t.dst_increment
155 .srcAddrIncrement = true, // Overriden by cyhal_dma_cfg_t.src_increment
156 .interrupt = true,
157 .preemptable = true,
158 .flipping = false,
159 #elif defined(CY_IP_MXSAXIDMAC)
160 .retrigger = CY_AXIDMAC_RETRIG_IM,
161 .interruptType = CY_AXIDMAC_DESCR, // Overridden by cyhal_dma_cfg_t.action
162 .triggerOutType = CY_AXIDMAC_DESCR_CHAIN, // Overridden by [en/dis]able_output()
163 .channelState = CY_AXIDMAC_CHANNEL_DISABLED,
164 .triggerInType = CY_AXIDMAC_DESCR, // Overridden by cyhal_dma_cfg_t.action & [dis]connect_digital()
165 .dataPrefetch = false,
166 .descriptorType = CY_AXIDMAC_1D_MEMORY_COPY, // Overriden by cyhal_dma_cfg_t.burst_size
167 .mCount = 1U, // Overriden by cyhal_dma_cfg_t.length
168 .srcXincrement = 0U, // Overriden by cyhal_dma_cfg_t.src_increment
169 .dstXincrement = 0U, // Overriden by cyhal_dma_cfg_t.dst_increment
170 .xCount = 1UL, // Overriden by cyhal_dma_cfg_t.length/burst_size
171 .srcYincrement = 0U,
172 .dstYincrement = 0U,
173 .yCount = 1UL,
174 .nextDescriptor = NULL,
175 #endif
176 };
177
178 /** Default dmac channel config */
179 #if defined(CY_IP_MXSAXIDMAC)
180 static const cy_stc_axidmac_channel_config_t _cyhal_dma_dmac_default_channel_config =
181 #else
182 static const cy_stc_dmac_channel_config_t _cyhal_dma_dmac_default_channel_config =
183 #endif
184 {
185 .priority = 1, // Overriden by config().priority
186 .enable = false,
187 #if defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC) || defined(CY_IP_MXAHBDMAC) || defined(CY_IP_MXSAXIDMAC)
188 .bufferable = false,
189 .descriptor = 0, // Overriden by config()
190 #elif defined(CY_IP_M0S8CPUSSV3_DMAC)
191 .descriptor = CY_DMAC_DESCRIPTOR_PING, // Overriden by config()
192 #endif
193 };
194
195 static bool _cyhal_dma_dmac_pm_transition_pending = false;
196
197 #if CYHAL_DRIVER_AVAILABLE_SYSPM
198 static bool _cyhal_dma_dmac_pm_callback(cyhal_syspm_callback_state_t state, cyhal_syspm_callback_mode_t mode, void* callback_arg);
199
200 static cyhal_syspm_callback_data_t _cyhal_dma_dmac_pm_callback_args = {
201 .callback = &_cyhal_dma_dmac_pm_callback,
202 .states = (cyhal_syspm_callback_state_t)(CYHAL_SYSPM_CB_CPU_DEEPSLEEP | CYHAL_SYSPM_CB_CPU_DEEPSLEEP_RAM | CYHAL_SYSPM_CB_SYSTEM_HIBERNATE),
203 .next = NULL,
204 .args = NULL,
205 .ignore_modes = (cyhal_syspm_callback_mode_t)(CYHAL_SYSPM_BEFORE_TRANSITION | CYHAL_SYSPM_AFTER_DS_WFI_TRANSITION),
206 };
207
_cyhal_dma_dmac_has_enabled(void)208 static bool _cyhal_dma_dmac_has_enabled(void)
209 {
210 for (uint8_t i = 0; i < NUM_DMAC_CHANNELS; i++)
211 if (NULL != _cyhal_dma_dmac_config_structs[i])
212 return true;
213 return false;
214 }
215
_cyhal_dma_dmac_pm_callback(cyhal_syspm_callback_state_t state,cyhal_syspm_callback_mode_t mode,void * callback_arg)216 static bool _cyhal_dma_dmac_pm_callback(cyhal_syspm_callback_state_t state, cyhal_syspm_callback_mode_t mode, void* callback_arg)
217 {
218 CY_UNUSED_PARAMETER(state);
219 CY_UNUSED_PARAMETER(callback_arg);
220 bool block_transition = false;
221 switch(mode)
222 {
223 case CYHAL_SYSPM_CHECK_READY:
224 for (uint8_t i = 0; (i < NUM_DMAC_CHANNELS) && !block_transition; i++)
225 {
226 block_transition |= (_cyhal_dma_dmac_config_structs[i] != NULL) && _cyhal_dma_dmac_is_busy(_cyhal_dma_dmac_config_structs[i]);
227 }
228 _cyhal_dma_dmac_pm_transition_pending = !block_transition;
229 break;
230 case CYHAL_SYSPM_CHECK_FAIL:
231 case CYHAL_SYSPM_AFTER_TRANSITION:
232 _cyhal_dma_dmac_pm_transition_pending = false;
233 break;
234 default:
235 CY_ASSERT(false);
236 break;
237 }
238 return _cyhal_dma_dmac_pm_transition_pending;
239 }
240 #endif
241
242 /** Gets the dmac configuration struct offset */
_cyhal_dma_dmac_get_cfg_offset(const cyhal_dma_t * obj)243 static inline uint8_t _cyhal_dma_dmac_get_cfg_offset(const cyhal_dma_t* obj)
244 {
245 return (obj->resource.block_num * NUM_DMAC0_CHANNELS) + obj->resource.channel_num;
246 }
247
248 /** Sets the dmac configuration struct */
_cyhal_dma_dmac_set_obj(cyhal_dma_t * obj)249 static inline void _cyhal_dma_dmac_set_obj(cyhal_dma_t *obj)
250 {
251 _cyhal_dma_dmac_config_structs[_cyhal_dma_dmac_get_cfg_offset(obj)] = obj;
252 }
253
254 /** Zeros the dmac configuration struct */
_cyhal_dma_dmac_free_obj(cyhal_dma_t * obj)255 static inline void _cyhal_dma_dmac_free_obj(cyhal_dma_t *obj)
256 {
257 _cyhal_dma_dmac_config_structs[_cyhal_dma_dmac_get_cfg_offset(obj)] = NULL;
258 }
259
260 /** Gets the dmac configuration struct from block and channel */
_cyhal_dma_dmac_get_obj(uint8_t block,uint8_t channel)261 static inline cyhal_dma_t* _cyhal_dma_dmac_get_obj(uint8_t block, uint8_t channel)
262 {
263 #if defined(CY_IP_MXSAXIDMAC)
264 return _cyhal_dma_dmac_config_structs[(block * APPCPUSS_AXIDMAC0_CH_NR) + channel];
265 #else
266 return _cyhal_dma_dmac_config_structs[(block * CPUSS_DMAC0_CH_NR) + channel];
267 #endif
268 }
269
270 /** Gets the dmac block number from irq number */
271 /** This should never be called from a non-dma IRQn */
_cyhal_dma_dmac_get_block_from_irqn(_cyhal_system_irq_t irqn)272 static inline uint8_t _cyhal_dma_dmac_get_block_from_irqn(_cyhal_system_irq_t irqn)
273 {
274 uint8_t diff = irqn - DMAC0_IRQn;
275 CY_ASSERT(diff < NUM_DMAC_CHANNELS);
276 if (diff < NUM_DMAC0_CHANNELS)
277 return 0;
278 #if (NUM_DMAC0_CHANNELS < NUM_DMAC_CHANNELS)
279 if (diff < NUM_DMAC_CHANNELS)
280 return 1;
281 #endif
282 else
283 {
284 // Should never reach here. Just silencing compiler warnings.
285 CY_ASSERT(false);
286 return 0xFF;
287 }
288 }
289
290 #if defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC) || defined(CY_IP_MXAHBDMAC) || defined(CY_IP_MXSAXIDMAC)
291 /** Gets the dmac channel number from irq number */
292 /** This should never be called from a non-dma IRQn */
_cyhal_dma_dmac_get_channel_from_irqn(_cyhal_system_irq_t irqn)293 static inline uint8_t _cyhal_dma_dmac_get_channel_from_irqn(_cyhal_system_irq_t irqn)
294 {
295 uint8_t diff = irqn - DMAC0_IRQn;
296 CY_ASSERT(diff < NUM_DMAC_CHANNELS);
297 if (diff < NUM_DMAC0_CHANNELS)
298 return diff;
299 #if (NUM_DMAC0_CHANNELS < NUM_DMAC_CHANNELS)
300 else
301 return diff - NUM_DMAC0_CHANNELS;
302 #endif
303 // Should never reach here. Just silencing compiler warnings.
304 CY_ASSERT(false);
305 return 0xFF;
306 }
307 #endif
308
309 /** Gets the irqn corresponding to a particular cyhal_dma_t config struct */
_cyhal_dma_dmac_get_irqn(cyhal_dma_t * obj)310 static inline _cyhal_system_irq_t _cyhal_dma_dmac_get_irqn(cyhal_dma_t *obj)
311 {
312 #if defined(CY_IP_M0S8CPUSSV3_DMAC)
313 CY_UNUSED_PARAMETER(obj);
314 /* This IP has a single ganged IRQ for all DMA channels */
315 return DMAC0_IRQn;
316 #else
317 return (_cyhal_system_irq_t)((uint8_t)DMAC0_IRQn + _cyhal_dma_dmac_get_cfg_offset(obj));
318 #endif
319 }
320
321 /** Gets the dmac base pointer from block number */
_cyhal_dma_dmac_get_base(uint8_t block_num)322 static inline cyhal_dmac_hw_type* _cyhal_dma_dmac_get_base(uint8_t block_num)
323 {
324 #if defined(CY_IP_MXSAXIDMAC)
325 CY_UNUSED_PARAMETER(block_num);
326 return SAXI_DMAC;
327 #elif defined(CPUSS_DMAC0_CH_NR) && !defined(CPUSS_DMAC1_CH_NR)
328 CY_UNUSED_PARAMETER(block_num);
329 return DMAC;
330 #elif defined(CPUSS_DMAC0_CH_NR) && defined(CPUSS_DMAC1_CH_NR)
331 return (block_num == 0) ? MXAHBDMAC0 : MXAHBDMAC1;
332 #else
333 //Should never get here
334 #error "Error: No corresponding base type"
335 #endif
336 }
337
338 /** Uses tables provided as part of the hal interconnect driver to determine mux
339 * trigger group and mux trigger index and then construct the trigger line
340 * input parameter to Cy_TrigMux_SwTrigger. */
_cyhal_dma_dmac_get_trigger_line(uint8_t block_num,uint8_t channel_num)341 static inline uint32_t _cyhal_dma_dmac_get_trigger_line(uint8_t block_num, uint8_t channel_num)
342 {
343 /* cyhal_dest_t triggers are guaranteed to be sorted by trigger type, block
344 * num, then channel num, therefore, we can just directly find the proper
345 * trigger by calculating an offset. */
346 #if defined(CY_IP_MXSAXIDMAC)
347 cyhal_dest_t trigger = (cyhal_dest_t)(CYHAL_TRIGGER_M55APPCPUSS_AXIDMAC_TR_IN0 + (block_num * APPCPUSS_AXIDMAC0_CH_NR) + channel_num);
348 #else
349 cyhal_dest_t trigger = (cyhal_dest_t)(CYHAL_TRIGGER_CPUSS_DMAC0_TR_IN0 + (block_num * CPUSS_DMAC0_CH_NR) + channel_num);
350 #endif
351 uint32_t trigger_line = 0;
352
353 /* One to one triggers have bit 8 set in cyhal_dest_to_mux but
354 * Cy_TrigMux_SwTrigger wants the trigger group field to have bit 5 set to
355 * denote one to one triggers. */
356 uint8_t trig_group = cyhal_dest_to_mux[trigger];
357 /* If hal one to one triggers bit is set: mask it out and set pdl one to
358 * one bit */
359 if(trig_group & _CYHAL_DMA_TRIGGERS_1TO1_MASK)
360 trig_group = (trig_group & ~_CYHAL_DMA_TRIGGERS_1TO1_MASK) | _CYHAL_DMA_PDL_TRIGGERS_1TO1_MASK;
361
362 /* Construct trigger line which consists of three fields packed into a
363 * uint32_t:
364 * Bits 30: Input/output bit. Set to 1 for output.
365 * Bits 12-8: Trigger group selection.
366 * Bits 7-0: Select the output trigger number in the trigger group. */
367 #if defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC) || defined(CY_IP_MXAHBDMAC) || defined(CY_IP_MXSAXIDMAC)
368 trigger_line = PERI_TR_CMD_OUT_SEL_Msk | ((uint32_t)trig_group << 8) | cyhal_mux_dest_index[trigger];
369 #elif defined(CY_IP_M0S8CPUSSV3_DMAC)
370 trigger_line = PERI_TR_CTL_TR_OUT_Msk | ((uint32_t)trig_group << 8) | cyhal_mux_dest_index[trigger];
371 #endif
372
373 #if (((CY_IP_MXSPERI_INSTANCES) == 2U) && defined(CY_IP_MXSAXIDMAC))
374 /* Bits 16: The PERI 1 instance.
375 The trigger input for AXIDMAC is located on PERI 1
376 */
377 trigger_line |= PERI_INSTANCE_1_IDENT_Msk;
378 #endif
379
380 return trigger_line;
381 }
382
383 #if defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC) || defined(CY_IP_MXAHBDMAC) || defined(CY_IP_MXSAXIDMAC)
384 /** Convert PDL interrupt cause to hal dma event */
_cyhal_dma_dmac_convert_interrupt_cause(cyhal_dma_t * obj,uint32_t cause)385 static inline cyhal_dma_event_t _cyhal_dma_dmac_convert_interrupt_cause(cyhal_dma_t *obj, uint32_t cause)
386 {
387 static const cyhal_dma_event_t hal[] =
388 {
389 CYHAL_DMA_TRANSFER_COMPLETE,
390 CYHAL_DMA_SRC_BUS_ERROR,
391 CYHAL_DMA_DST_BUS_ERROR,
392 CYHAL_DMA_SRC_MISAL,
393 CYHAL_DMA_DST_MISAL,
394 CYHAL_DMA_CURR_PTR_NULL,
395 CYHAL_DMA_ACTIVE_CH_DISABLED,
396 CYHAL_DMA_DESCR_BUS_ERROR
397 };
398
399 cyhal_dma_event_t hal_rslt = CYHAL_DMA_NO_INTR;
400 for (uint8_t i = 0; cause > 0 && i < sizeof(hal)/sizeof(cyhal_dma_event_t); i++)
401 {
402 if ((cause & (1 << i)) > 0)
403 hal_rslt |= hal[i];
404 }
405
406 if ((uint32_t)(hal_rslt & CYHAL_DMA_TRANSFER_COMPLETE) > 0 && obj->expected_bursts > 0)
407 {
408 obj->expected_bursts--;
409 if (0 == obj->expected_bursts)
410 {
411 hal_rslt |= CYHAL_DMA_DESCRIPTOR_COMPLETE;
412 #if defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC) || defined(CY_IP_MXAHBDMAC)
413 obj->expected_bursts = (GET_RESOURCE_DATA(obj->descriptor_config).interruptType == _CYHAL_DMAC_X_LOOP)
414 ? GET_RESOURCE_DATA(obj->descriptor_config).yCount
415 : 1;
416 #elif defined(CY_IP_MXSAXIDMAC)
417 obj->expected_bursts = (GET_RESOURCE_DATA(obj->descriptor_config).interruptType == _CYHAL_DMAC_M_LOOP)
418 ? GET_RESOURCE_DATA(obj->descriptor_config).xCount
419 : 1;
420 #else
421 obj->expected_bursts = 1;
422 #endif
423 }
424 }
425
426 return hal_rslt;
427 }
428 #endif
429
430 /** DMAC irq handler */
_cyhal_dma_dmac_irq_handler(void)431 static void _cyhal_dma_dmac_irq_handler(void)
432 {
433 /* Use irqn to get appropriate config structure */
434 _cyhal_system_irq_t irqn = _cyhal_irq_get_active();
435 uint8_t block = _cyhal_dma_dmac_get_block_from_irqn(irqn);
436 #if defined(CY_IP_MXSAXIDMAC)
437 SAXI_DMAC_Type* base = _cyhal_dma_dmac_get_base(block);
438 #else
439 DMAC_Type* base = _cyhal_dma_dmac_get_base(block);
440 #endif
441 #if defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC) || defined(CY_IP_MXAHBDMAC) || defined(CY_IP_MXSAXIDMAC)
442 uint8_t channel = _cyhal_dma_dmac_get_channel_from_irqn(irqn);
443 cyhal_dma_t *obj = _cyhal_dma_dmac_get_obj(block, channel);
444
445 /* Get interrupt type and call users event callback if they have enabled that event */
446 uint32_t cause = _cyhal_dmac_channel_get_interrupt_status_masked(base, channel);
447 cyhal_dma_event_t event_type = _cyhal_dma_dmac_convert_interrupt_cause(obj, cause);
448 uint32_t events_to_callback = event_type & obj->irq_cause;
449 if(obj->callback_data.callback != NULL && events_to_callback)
450 {
451 ((cyhal_dma_event_callback_t)obj->callback_data.callback)(obj->callback_data.callback_arg, (cyhal_dma_event_t)events_to_callback);
452 }
453
454 /* Clear all interrupts */
455 #if defined(CY_IP_MXSAXIDMAC)
456 Cy_AXIDMAC_Channel_ClearInterrupt(base, channel, CY_AXIDMAC_INTR_MASK);
457 #else
458 Cy_DMAC_Channel_ClearInterrupt(base, channel, CY_DMAC_INTR_MASK);
459 #endif
460 #elif defined(CY_IP_M0S8CPUSSV3_DMAC)
461 uint32_t channels = Cy_DMAC_GetInterruptStatusMasked(base);
462 for(int i = 0 ; ((uint32_t)(1 << i)) <= channels ; i++)
463 {
464 cyhal_dma_t *obj = _cyhal_dma_dmac_get_obj(block, i);
465 if (obj != NULL)
466 {
467 if (((channels & (1 << i)) != 0) && (obj->callback_data.callback != NULL))
468 {
469 ((cyhal_dma_event_callback_t)obj->callback_data.callback)(obj->callback_data.callback_arg, CYHAL_DMA_TRANSFER_COMPLETE);
470 }
471 }
472 }
473 Cy_DMAC_ClearInterrupt(_cyhal_dma_dmac_get_base(block), channels);
474 #endif
475 }
476
_cyhal_dma_dmac_get_src(uint8_t block_num,uint8_t channel_num)477 static cyhal_source_t _cyhal_dma_dmac_get_src(uint8_t block_num, uint8_t channel_num)
478 {
479 #if defined(CY_IP_MXSAXIDMAC)
480 return (cyhal_source_t)_CYHAL_TRIGGER_CREATE_SOURCE((_CYHAL_TRIGGER_M55APPCPUSS_AXIDMAC_TR_OUT0 + (block_num * APPCPUSS_AXIDMAC0_CH_NR) + channel_num), CYHAL_SIGNAL_TYPE_EDGE);
481 #else
482 return (cyhal_source_t)_CYHAL_TRIGGER_CREATE_SOURCE((_CYHAL_TRIGGER_CPUSS_DMAC0_TR_OUT0 + (block_num * CPUSS_DMAC0_CH_NR) + channel_num), CYHAL_SIGNAL_TYPE_EDGE);
483 #endif
484 }
485
_cyhal_dma_dmac_get_dest(uint8_t block_num,uint8_t channel_num)486 static cyhal_dest_t _cyhal_dma_dmac_get_dest(uint8_t block_num, uint8_t channel_num)
487 {
488 #if defined(CY_IP_MXSAXIDMAC)
489 return (cyhal_dest_t)(CYHAL_TRIGGER_M55APPCPUSS_AXIDMAC_TR_IN0 + (block_num * APPCPUSS_AXIDMAC0_CH_NR) + channel_num);
490 #else
491 return (cyhal_dest_t)(CYHAL_TRIGGER_CPUSS_DMAC0_TR_IN0 + (block_num * CPUSS_DMAC0_CH_NR) + channel_num);
492 #endif
493 }
494
_cyhal_dma_dmac_stage(cyhal_dma_t * obj)495 static cy_rslt_t _cyhal_dma_dmac_stage(cyhal_dma_t *obj)
496 {
497 cyhal_dmac_hw_type* base = _cyhal_dma_dmac_get_base(obj->resource.block_num);
498 #if defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC) || defined(CY_IP_MXAHBDMAC) || defined(CY_IP_MXSAXIDMAC)
499 #if defined(CY_IP_MXSAXIDMAC)
500 cy_en_axidmac_descriptor_type_t descr_type = GET_RESOURCE_DATA(&obj->descriptor_config)->descriptorType;
501 GET_RESOURCE_DATA(&obj->descriptor_config)->descriptorType = CY_AXIDMAC_3D_MEMORY_COPY;
502 cy_rslt_t rslt = Cy_AXIDMAC_Descriptor_Init(GET_RESOURCE_DATA(&obj->descriptor), GET_RESOURCE_DATA(&obj->descriptor_config));
503 Cy_AXIDMAC_Descriptor_SetDescriptorType(GET_RESOURCE_DATA(&obj->descriptor), descr_type);
504 GET_RESOURCE_DATA(&obj->descriptor_config)->descriptorType = descr_type;
505 #else
506 cy_rslt_t rslt = Cy_DMAC_Descriptor_Init(GET_RESOURCE_DATA(&obj->descriptor), GET_RESOURCE_DATA(&obj->descriptor_config));
507 #endif
508 #elif defined(CY_IP_M0S8CPUSSV3_DMAC)
509 cy_rslt_t rslt = Cy_DMAC_Descriptor_Init(base, obj->resource.channel_num, obj->descriptor, GET_RESOURCE_DATA(&obj->descriptor_config));
510 #endif
511 #if defined(CY_IP_MXSAXIDMAC)
512 if(CY_AXIDMAC_SUCCESS != rslt)
513 #else
514 if(CY_DMAC_SUCCESS != rslt)
515 #endif
516 return CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER;
517
518 /* Setup channel and enable */
519 #if defined(CY_IP_MXSAXIDMAC)
520 if(CY_AXIDMAC_SUCCESS != Cy_AXIDMAC_Channel_Init(base, obj->resource.channel_num, GET_RESOURCE_DATA(&obj->channel_config)))
521 #else
522 if(CY_DMAC_SUCCESS != Cy_DMAC_Channel_Init(base, obj->resource.channel_num, GET_RESOURCE_DATA(&obj->channel_config)))
523 #endif
524 return CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER;
525
526 #if defined(CY_IP_MXSAXIDMAC)
527 Cy_AXIDMAC_Channel_SetInterruptMask(base, obj->resource.channel_num, CY_AXIDMAC_INTR_MASK);
528 #elif defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC) || defined(CY_IP_MXAHBDMAC)
529 Cy_DMAC_Channel_SetInterruptMask(base, obj->resource.channel_num, CY_DMAC_INTR_MASK);
530 #endif
531 #if defined(CY_IP_MXSAXIDMAC)
532 Cy_AXIDMAC_Enable(base);
533 #else
534 Cy_DMAC_Enable(base);
535 #endif
536
537 #if defined(CY_IP_MXSAXIDMAC)
538 /* src_misal and dst_misal interrupts are triggered immediately on enable
539 * so return those errors here */
540 uint32_t status = Cy_AXIDMAC_Channel_GetInterruptStatus(base, obj->resource.channel_num);
541 if((status & CY_AXIDMAC_INTR_INVALID_DESCR_TYPE))
542 {
543 /* Clear all interrupts and return error */
544 Cy_AXIDMAC_Channel_ClearInterrupt(base, obj->resource.channel_num, CY_AXIDMAC_INTR_MASK);
545 return CYHAL_DMA_RSLT_ERR_INVALID_ALIGNMENT;
546 }
547 #elif defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC) || defined(CY_IP_MXAHBDMAC)
548 /* src_misal and dst_misal interrupts are triggered immediately on enable
549 * so return those errors here */
550 uint32_t status = Cy_DMAC_Channel_GetInterruptStatus(base, obj->resource.channel_num);
551 if((status & CY_DMAC_INTR_SRC_MISAL) ||
552 (status & CY_DMAC_INTR_DST_MISAL))
553 {
554 /* Clear all interrupts and return error */
555 Cy_DMAC_Channel_ClearInterrupt(base, obj->resource.channel_num, CY_DMAC_INTR_MASK);
556 return CYHAL_DMA_RSLT_ERR_INVALID_ALIGNMENT;
557 }
558 #endif
559
560 /* Enable interrupt for this channel; preserve user priority if they enabled an interrupt */
561 _cyhal_system_irq_t irqn = _cyhal_dma_dmac_get_irqn(obj);
562 uint32_t priority = (CYHAL_DMA_NO_INTR == obj->irq_cause)
563 ? CYHAL_ISR_PRIORITY_DEFAULT
564 : _cyhal_irq_get_priority(irqn);
565
566 if(CY_RSLT_SUCCESS != _cyhal_irq_register(irqn, priority, _cyhal_dma_dmac_irq_handler))
567 return CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER;
568 _cyhal_irq_enable(irqn);
569
570 #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
571 SCB_CleanDCache_by_Addr((void *)&(obj->descriptor), sizeof(obj->descriptor));
572 #endif /* defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) */
573
574 return CY_RSLT_SUCCESS;
575 }
576
_cyhal_dma_dmac_init(cyhal_dma_t * obj,cyhal_source_t * src,cyhal_dest_t * dest,uint8_t priority)577 cy_rslt_t _cyhal_dma_dmac_init(cyhal_dma_t *obj, cyhal_source_t *src, cyhal_dest_t *dest, uint8_t priority)
578 {
579
580 #if defined(CY_IP_MXSAXIDMAC)
581 if(!CY_AXIDMAC_IS_PRIORITY_VALID(priority))
582 #else
583 if(!CY_DMAC_IS_PRIORITY_VALID(priority))
584 #endif
585 return CYHAL_DMA_RSLT_ERR_INVALID_PRIORITY;
586
587 if (_cyhal_dma_dmac_pm_transition_pending)
588 {
589 return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
590 }
591
592 cy_rslt_t rslt = _cyhal_hwmgr_allocate_with_connection(
593 CYHAL_RSC_DMA, src, dest, _cyhal_dma_dmac_get_src, _cyhal_dma_dmac_get_dest, &obj->resource);
594 if(rslt != CY_RSLT_SUCCESS)
595 return rslt;
596
597 obj->callback_data.callback = NULL;
598
599 /* Setup descriptor and channel configs */
600 GET_RESOURCE_DATA(obj->descriptor_config) = _cyhal_dma_dmac_default_descriptor_config;
601 GET_RESOURCE_DATA(obj->channel_config) = _cyhal_dma_dmac_default_channel_config;
602 #if defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC) || defined(CY_IP_MXAHBDMAC) || defined(CY_IP_MXSAXIDMAC)
603 GET_RESOURCE_DATA(obj->channel_config).descriptor = GET_RESOURCE_DATA(&obj->descriptor);
604 #elif defined(CY_IP_M0S8CPUSSV3_DMAC)
605 obj->descriptor = obj->channel_config.descriptor;
606 #endif
607 GET_RESOURCE_DATA(obj->channel_config).priority = priority;
608
609 #if CYHAL_DRIVER_AVAILABLE_SYSPM
610 if (!_cyhal_dma_dmac_has_enabled())
611 {
612 _cyhal_syspm_register_peripheral_callback(&_cyhal_dma_dmac_pm_callback_args);
613 }
614 #endif
615
616 _cyhal_dma_dmac_set_obj(obj);
617
618 return CY_RSLT_SUCCESS;
619 }
620
_cyhal_dma_dmac_init_cfg(cyhal_dma_t * obj,const cyhal_dma_configurator_t * cfg)621 cy_rslt_t _cyhal_dma_dmac_init_cfg(cyhal_dma_t *obj, const cyhal_dma_configurator_t *cfg)
622 {
623 if (_cyhal_dma_dmac_pm_transition_pending)
624 {
625 return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
626 }
627
628 obj->resource = *(cfg->resource);
629 obj->callback_data.callback = NULL;
630
631 /* Setup descriptor and channel configs */
632 #if defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC) || defined(CY_IP_MXAHBDMAC) || defined(CY_IP_MXSAXIDMAC)
633 obj->descriptor_config.dmac = *(cfg->dmac_descriptor_config);
634 obj->channel_config.dmac = *(cfg->dmac_channel_config);
635 GET_RESOURCE_DATA(obj->channel_config).descriptor = GET_RESOURCE_DATA(&obj->descriptor);
636 #if defined(CY_IP_MXSAXIDMAC)
637 obj->expected_bursts = cfg->dmac_descriptor_config->xCount;
638 #else
639 obj->expected_bursts = cfg->dmac_descriptor_config->yCount;
640 #endif
641 #elif defined(CY_IP_M0S8CPUSSV3_DMAC)
642 obj->descriptor_config = *(cfg->descriptor_config);
643 obj->channel_config = *(cfg->channel_config);
644 obj->descriptor = obj->channel_config.descriptor;
645 obj->expected_bursts = 1;
646 #endif
647
648 #if CYHAL_DRIVER_AVAILABLE_SYSPM
649 if (!_cyhal_dma_dmac_has_enabled())
650 {
651 _cyhal_syspm_register_peripheral_callback(&_cyhal_dma_dmac_pm_callback_args);
652 }
653 #endif
654
655 _cyhal_dma_dmac_set_obj(obj);
656
657 return _cyhal_dma_dmac_stage(obj);
658 }
659
_cyhal_dma_dmac_free(cyhal_dma_t * obj)660 void _cyhal_dma_dmac_free(cyhal_dma_t *obj)
661 {
662 cyhal_dmac_hw_type* base = _cyhal_dma_dmac_get_base(obj->resource.block_num);
663 #if defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC) || defined(CY_IP_MXAHBDMAC)
664 Cy_DMAC_Descriptor_DeInit(GET_RESOURCE_DATA(&obj->descriptor));
665 Cy_DMAC_Channel_DeInit(base, obj->resource.channel_num);
666 #elif defined(CY_IP_M0S8CPUSSV3_DMAC)
667 Cy_DMAC_Descriptor_DeInit(base, obj->resource.channel_num, obj->descriptor);
668 Cy_DMAC_Channel_DeInit(base, obj->resource.channel_num);
669 #elif defined(CY_IP_MXSAXIDMAC)
670 Cy_AXIDMAC_Descriptor_DeInit(GET_RESOURCE_DATA(&obj->descriptor));
671 Cy_AXIDMAC_Channel_DeInit(base, obj->resource.channel_num);
672 #endif
673
674 _cyhal_irq_free(_cyhal_dma_dmac_get_irqn(obj));
675
676 _cyhal_dma_dmac_free_obj(obj);
677
678 #if CYHAL_DRIVER_AVAILABLE_SYSPM
679 if (!_cyhal_dma_dmac_has_enabled())
680 {
681 _cyhal_syspm_unregister_peripheral_callback(&_cyhal_dma_dmac_pm_callback_args);
682 _cyhal_dma_dmac_pm_transition_pending = false;
683 }
684 #endif
685 }
686
687 /* Initialize descriptor, initialize channel, enable channel, enable channel
688 * interrupt, and enable DMAC controller */
_cyhal_dma_dmac_configure(cyhal_dma_t * obj,const cyhal_dma_cfg_t * cfg)689 cy_rslt_t _cyhal_dma_dmac_configure(cyhal_dma_t *obj, const cyhal_dma_cfg_t *cfg)
690 {
691 uint32_t total_length = cfg->length;
692
693 /* Do not reconfigure if transfer is pending/active already */
694 if(_cyhal_dma_dmac_is_busy(obj))
695 return CYHAL_DMA_RSLT_ERR_CHANNEL_BUSY;
696
697 // DMAC only supports <=65536 byte burst and <=65536 bytes per burst
698 if ((cfg->burst_size > 65536) ||
699 ((cfg->burst_size <= 1) && (cfg->length > 65536)) ||
700 ((cfg->burst_size > 0) && (cfg->length > (cfg->burst_size * 65536))))
701 return CYHAL_DMA_RSLT_ERR_INVALID_TRANSFER_SIZE;
702
703 #if defined(CY_IP_M0S8CPUSSV3_DMAC)
704 // PSoC™ 4 devices do not support automatically disabling the channel on completion
705 if ((cfg->action == CYHAL_DMA_TRANSFER_BURST_DISABLE) ||
706 (cfg->action == CYHAL_DMA_TRANSFER_FULL_DISABLE))
707 {
708 return CYHAL_DMA_RSLT_FATAL_UNSUPPORTED_HARDWARE;
709 }
710 #endif
711
712 GET_RESOURCE_DATA(obj->descriptor_config).srcAddress = (void*)cfg->src_addr;
713 GET_RESOURCE_DATA(obj->descriptor_config).dstAddress = (void*)cfg->dst_addr;
714
715 if ((cfg->transfer_width != 8) && (cfg->transfer_width != 16) && (cfg->transfer_width != 32))
716 return CYHAL_DMA_RSLT_ERR_INVALID_TRANSFER_WIDTH;
717
718 #if !defined(CY_IP_MXSAXIDMAC)
719 total_length = cfg->length;
720
721 if(cfg->transfer_width == 8)
722 GET_RESOURCE_DATA(obj->descriptor_config).dataSize = CY_DMAC_BYTE;
723 else if(cfg->transfer_width == 16)
724 GET_RESOURCE_DATA(obj->descriptor_config).dataSize = CY_DMAC_HALFWORD;
725 else if(cfg->transfer_width == 32)
726 GET_RESOURCE_DATA(obj->descriptor_config).dataSize = CY_DMAC_WORD;
727 else
728 return CYHAL_DMA_RSLT_ERR_INVALID_TRANSFER_WIDTH;
729
730 /* By default, transfer what the user set for dataSize. However, if transfering between memory
731 * and a peripheral, make sure the peripheral access is using words. */
732 GET_RESOURCE_DATA(obj->descriptor_config).srcTransferSize =
733 GET_RESOURCE_DATA(obj->descriptor_config).dstTransferSize = CY_DMAC_TRANSFER_SIZE_DATA;
734 if (obj->direction == CYHAL_DMA_DIRECTION_PERIPH2MEM)
735 GET_RESOURCE_DATA(obj->descriptor_config).srcTransferSize = CY_DMAC_TRANSFER_SIZE_WORD;
736 else if (obj->direction == CYHAL_DMA_DIRECTION_MEM2PERIPH)
737 GET_RESOURCE_DATA(obj->descriptor_config).dstTransferSize = CY_DMAC_TRANSFER_SIZE_WORD;
738 #else
739 /* Need to convert number of elements to the total number of bytes for transfer */
740 total_length = cfg->length * (cfg->transfer_width / 8);
741 if (!CY_AXIDMAC_IS_LOOP_COUNT_VALID(total_length))
742 {
743 return CYHAL_DMA_RSLT_ERR_INVALID_TRANSFER_SIZE;
744 }
745 #endif
746
747 #if defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC) || defined(CY_IP_MXAHBDMAC) || defined(CY_IP_MXSAXIDMAC)
748 GET_RESOURCE_DATA(obj->descriptor_config).nextDescriptor = GET_RESOURCE_DATA(&obj->descriptor);
749 if ((cfg->action == CYHAL_DMA_TRANSFER_BURST) || (cfg->action == CYHAL_DMA_TRANSFER_FULL))
750 {
751 GET_RESOURCE_DATA(obj->descriptor_config).channelState = _CYHAL_DMAC_CHANNEL_ENABLED;
752 }
753 else
754 {
755 GET_RESOURCE_DATA(obj->descriptor_config).channelState = _CYHAL_DMAC_CHANNEL_DISABLED;
756 }
757
758 GET_RESOURCE_DATA(obj->descriptor_config).srcXincrement = cfg->src_increment;
759 GET_RESOURCE_DATA(obj->descriptor_config).dstXincrement = cfg->dst_increment;
760
761 /* Setup 2D transfer if burst_size is being used otherwise set up 1D
762 * transfer */
763 if(cfg->burst_size != 0)
764 {
765 /* Length must be a multiple of burst_size */
766 if(total_length % cfg->burst_size != 0)
767 return CYHAL_DMA_RSLT_ERR_INVALID_BURST_SIZE;
768
769 #if defined(CY_IP_MXSAXIDMAC)
770 GET_RESOURCE_DATA(obj->descriptor_config).descriptorType = CY_AXIDMAC_2D_MEMORY_COPY;
771 GET_RESOURCE_DATA(obj->descriptor_config).mCount = cfg->burst_size * (cfg->transfer_width / 8);
772 GET_RESOURCE_DATA(obj->descriptor_config).xCount = (cfg->length / cfg->burst_size);
773 GET_RESOURCE_DATA(obj->descriptor_config).srcXincrement = cfg->src_increment * cfg->burst_size * (cfg->transfer_width / 8);
774 GET_RESOURCE_DATA(obj->descriptor_config).dstXincrement = cfg->dst_increment * cfg->burst_size * (cfg->transfer_width / 8);
775 #else
776 GET_RESOURCE_DATA(obj->descriptor_config).descriptorType = CY_DMAC_2D_TRANSFER;
777 GET_RESOURCE_DATA(obj->descriptor_config).xCount = cfg->burst_size;
778 GET_RESOURCE_DATA(obj->descriptor_config).yCount = total_length / cfg->burst_size;
779 GET_RESOURCE_DATA(obj->descriptor_config).srcYincrement = cfg->src_increment * cfg->burst_size;
780 GET_RESOURCE_DATA(obj->descriptor_config).dstYincrement = cfg->dst_increment * cfg->burst_size;
781 #endif
782
783 }
784 else
785 {
786 #if defined(CY_IP_MXSAXIDMAC)
787 GET_RESOURCE_DATA(obj->descriptor_config).descriptorType = CY_AXIDMAC_1D_MEMORY_COPY;
788 GET_RESOURCE_DATA(obj->descriptor_config).mCount = total_length;
789 #else
790 GET_RESOURCE_DATA(obj->descriptor_config).descriptorType = CY_DMAC_1D_TRANSFER;
791 GET_RESOURCE_DATA(obj->descriptor_config).xCount = total_length;
792 #endif
793 }
794
795 /* If burst action, configure trigger and interrupt actions */
796 if (cfg->burst_size != 0 &&
797 (cfg->action == CYHAL_DMA_TRANSFER_BURST || cfg->action == CYHAL_DMA_TRANSFER_BURST_DISABLE))
798 {
799 #if defined(CY_IP_MXSAXIDMAC)
800 obj->expected_bursts = GET_RESOURCE_DATA(obj->descriptor_config).xCount;
801 GET_RESOURCE_DATA(obj->descriptor_config).interruptType = _CYHAL_DMAC_M_LOOP;
802 if (obj->source == _CYHAL_TRIGGER_CPUSS_ZERO) // If not overridden by connect_digital()
803 GET_RESOURCE_DATA(obj->descriptor_config).triggerInType = _CYHAL_DMAC_M_LOOP;
804 #else
805 obj->expected_bursts = GET_RESOURCE_DATA(obj->descriptor_config).yCount;
806 GET_RESOURCE_DATA(obj->descriptor_config).interruptType = _CYHAL_DMAC_X_LOOP;
807 if (obj->source == _CYHAL_TRIGGER_CPUSS_ZERO) // If not overridden by connect_digital()
808 GET_RESOURCE_DATA(obj->descriptor_config).triggerInType = _CYHAL_DMAC_X_LOOP;
809 #endif
810 }
811 else
812 {
813 obj->expected_bursts = 1;
814 GET_RESOURCE_DATA(obj->descriptor_config).interruptType = _CYHAL_DMAC_DESCR;
815 if (obj->source == _CYHAL_TRIGGER_CPUSS_ZERO) // If not overridden by connect_digital()
816 GET_RESOURCE_DATA(obj->descriptor_config).triggerInType = _CYHAL_DMAC_DESCR;
817 }
818 #elif defined(CY_IP_M0S8CPUSSV3_DMAC)
819 if(cfg->burst_size != 0)
820 {
821 return CYHAL_DMA_RSLT_ERR_INVALID_BURST_SIZE;
822 }
823 else
824 {
825 GET_RESOURCE_DATA(obj->descriptor_config).dataCount = total_length;
826 GET_RESOURCE_DATA(obj->descriptor_config).srcAddrIncrement = cfg->src_increment;
827 GET_RESOURCE_DATA(obj->descriptor_config).dstAddrIncrement = cfg->dst_increment;
828 obj->expected_bursts = 1;
829 }
830 #endif
831
832 return _cyhal_dma_dmac_stage(obj);
833 }
834
_cyhal_dma_dmac_enable(cyhal_dma_t * obj)835 cy_rslt_t _cyhal_dma_dmac_enable(cyhal_dma_t *obj)
836 {
837 cyhal_dmac_hw_type* base = _cyhal_dma_dmac_get_base(obj->resource.block_num);
838 _cyhal_dmac_channel_enable(base, obj->resource.channel_num);
839 return CY_RSLT_SUCCESS;
840 }
841
_cyhal_dma_dmac_disable(cyhal_dma_t * obj)842 cy_rslt_t _cyhal_dma_dmac_disable(cyhal_dma_t *obj)
843 {
844 cyhal_dmac_hw_type* base = _cyhal_dma_dmac_get_base(obj->resource.block_num);
845 _cyhal_dmac_channel_disable(base, obj->resource.channel_num);
846 return CY_RSLT_SUCCESS;
847 }
848
_cyhal_dma_dmac_start_transfer(cyhal_dma_t * obj)849 cy_rslt_t _cyhal_dma_dmac_start_transfer(cyhal_dma_t *obj)
850 {
851 /* Return warning if channel is busy */
852 if(_cyhal_dma_dmac_is_busy(obj))
853 return CYHAL_DMA_RSLT_WARN_TRANSFER_ALREADY_STARTED;
854
855 if (_cyhal_dma_dmac_pm_transition_pending)
856 return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
857
858 uint32_t trigline = _cyhal_dma_dmac_get_trigger_line(obj->resource.block_num, obj->resource.channel_num);
859 cy_en_trigmux_status_t trig_status = Cy_TrigMux_SwTrigger(trigline, CY_TRIGGER_TWO_CYCLES);
860
861 /* Also return warning if SW trigger is already initiated but DMA hardware
862 * has not seen it yet */
863 if(trig_status == CY_TRIGMUX_INVALID_STATE)
864 return CYHAL_DMA_RSLT_WARN_TRANSFER_ALREADY_STARTED;
865 else
866 return CY_RSLT_SUCCESS;
867 }
868
_cyhal_dma_dmac_enable_event(cyhal_dma_t * obj,cyhal_dma_event_t event,uint8_t intr_priority,bool enable)869 void _cyhal_dma_dmac_enable_event(cyhal_dma_t *obj, cyhal_dma_event_t event, uint8_t intr_priority, bool enable)
870 {
871 #if defined (CY_IP_M0S8CPUSSV3_DMAC)
872 DMAC_Type *base = _cyhal_dma_dmac_get_base(obj->resource.block_num);
873 uint32_t mask = Cy_DMAC_GetInterruptMask(base);
874 #endif
875 if(enable)
876 {
877 #if defined (CY_IP_M0S8CPUSSV3_DMAC)
878 Cy_DMAC_SetInterruptMask(base, mask | (1 << obj->resource.channel_num));
879 #endif
880 obj->irq_cause |= event;
881 }
882 else
883 {
884 #if defined (CY_IP_M0S8CPUSSV3_DMAC)
885 Cy_DMAC_SetInterruptMask(base, mask & ~(1 << obj->resource.channel_num));
886 #endif
887 obj->irq_cause &= ~event;
888 }
889
890 _cyhal_irq_set_priority(_cyhal_dma_dmac_get_irqn(obj), intr_priority);
891 }
892
_cyhal_dma_dmac_is_busy(cyhal_dma_t * obj)893 bool _cyhal_dma_dmac_is_busy(cyhal_dma_t *obj)
894 {
895 /* The value is a bit field of all pending or active channels */
896 return _cyhal_dmac_get_active_channel(_cyhal_dma_dmac_get_base(obj->resource.block_num)) & (1 << obj->resource.channel_num);
897 }
898 #if defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC) || defined(CY_IP_MXAHBDMAC) || defined(CY_IP_MXSAXIDMAC)
899 #if defined(CY_IP_MXSAXIDMAC)
_cyhal_convert_input_t(cyhal_dma_input_t input)900 static cy_en_axidmac_trigger_type_t _cyhal_convert_input_t(cyhal_dma_input_t input)
901 {
902 switch(input)
903 {
904 case CYHAL_DMA_INPUT_TRIGGER_SINGLE_ELEMENT:
905 return CY_AXIDMAC_M_LOOP;
906 case CYHAL_DMA_INPUT_TRIGGER_SINGLE_BURST:
907 return CY_AXIDMAC_X_LOOP;
908 case CYHAL_DMA_INPUT_TRIGGER_ALL_ELEMENTS:
909 return CY_AXIDMAC_DESCR;
910 default:
911 // Should never reach here. Just silencing compiler warnings.
912 CY_ASSERT(false);
913 return CY_AXIDMAC_DESCR;
914 }
915 }
916 #elif defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC) || defined(CY_IP_MXAHBDMAC)
_cyhal_convert_input_t(cyhal_dma_input_t input)917 static cy_en_dmac_trigger_type_t _cyhal_convert_input_t(cyhal_dma_input_t input)
918 {
919 switch(input)
920 {
921 case CYHAL_DMA_INPUT_TRIGGER_SINGLE_ELEMENT:
922 return CY_DMAC_1ELEMENT;
923 case CYHAL_DMA_INPUT_TRIGGER_SINGLE_BURST:
924 return CY_DMAC_X_LOOP;
925 case CYHAL_DMA_INPUT_TRIGGER_ALL_ELEMENTS:
926 return CY_DMAC_DESCR;
927 default:
928 // Should never reach here. Just silencing compiler warnings.
929 CY_ASSERT(false);
930 return CY_DMAC_DESCR;
931 }
932 }
933 #endif
934
935 #if defined(CY_IP_MXSAXIDMAC)
_cyhal_convert_output_t(cyhal_dma_output_t output)936 static cy_en_axidmac_trigger_type_t _cyhal_convert_output_t(cyhal_dma_output_t output)
937 {
938 switch(output)
939 {
940 case CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_ELEMENT:
941 return CY_AXIDMAC_M_LOOP;
942 case CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_BURST:
943 return CY_AXIDMAC_X_LOOP;
944 case CYHAL_DMA_OUTPUT_TRIGGER_ALL_ELEMENTS:
945 return CY_AXIDMAC_DESCR;
946 default:
947 // Should never reach here. Just silencing compiler warnings.
948 CY_ASSERT(false);
949 return CY_AXIDMAC_DESCR;
950 }
951 }
952 #else
_cyhal_convert_output_t(cyhal_dma_output_t output)953 static cy_en_dmac_trigger_type_t _cyhal_convert_output_t(cyhal_dma_output_t output)
954 {
955 switch(output)
956 {
957 case CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_ELEMENT:
958 return CY_DMAC_1ELEMENT;
959 case CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_BURST:
960 return CY_DMAC_X_LOOP;
961 case CYHAL_DMA_OUTPUT_TRIGGER_ALL_ELEMENTS:
962 return CY_DMAC_DESCR;
963 default:
964 // Should never reach here. Just silencing compiler warnings.
965 CY_ASSERT(false);
966 return CY_DMAC_DESCR;
967 }
968 }
969 #endif
970
_cyhal_dma_dmac_connect_digital(cyhal_dma_t * obj,cyhal_source_t source,cyhal_dma_input_t input)971 cy_rslt_t _cyhal_dma_dmac_connect_digital(cyhal_dma_t *obj, cyhal_source_t source, cyhal_dma_input_t input)
972 {
973 if(input != CYHAL_DMA_INPUT_TRIGGER_SINGLE_ELEMENT &&
974 input != CYHAL_DMA_INPUT_TRIGGER_SINGLE_BURST &&
975 input != CYHAL_DMA_INPUT_TRIGGER_ALL_ELEMENTS)
976 return CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER;
977
978 obj->descriptor_config.dmac.triggerInType = _cyhal_convert_input_t(input);
979
980 _cyhal_dmac_channel_disable(_cyhal_dma_dmac_get_base(obj->resource.block_num), obj->resource.channel_num);
981
982 obj->descriptor.dmac.ctl &= ~_CYHAL_DMAC_CH_DESCR_CTL_TR_IN_TYPE_Msk;
983 obj->descriptor.dmac.ctl |= _VAL2FLD(_CYHAL_DMAC_CH_DESCR_CTL_TR_IN_TYPE, obj->descriptor_config.dmac.triggerInType);
984 #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
985 SCB_CleanDCache_by_Addr((void *)&(obj->descriptor), sizeof(obj->descriptor));
986 #endif /* defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) */
987
988 _cyhal_dmac_channel_enable(_cyhal_dma_dmac_get_base(obj->resource.block_num), obj->resource.channel_num);
989
990 cyhal_dest_t dest = _cyhal_dma_dmac_get_dest(obj->resource.block_num, obj->resource.channel_num);
991
992 return _cyhal_connect_signal(source, dest);
993 }
994
_cyhal_dma_dmac_enable_output(cyhal_dma_t * obj,cyhal_dma_output_t output,cyhal_source_t * source)995 cy_rslt_t _cyhal_dma_dmac_enable_output(cyhal_dma_t *obj, cyhal_dma_output_t output, cyhal_source_t *source)
996 {
997 if(output != CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_ELEMENT &&
998 output != CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_BURST &&
999 output != CYHAL_DMA_OUTPUT_TRIGGER_ALL_ELEMENTS)
1000 return CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER;
1001
1002 _cyhal_dmac_channel_disable(_cyhal_dma_dmac_get_base(obj->resource.block_num), obj->resource.channel_num);
1003
1004 obj->descriptor.dmac.ctl &= ~_CYHAL_DMAC_CH_DESCR_CTL_TR_OUT_TYPE_Msk;
1005 obj->descriptor.dmac.ctl |= _VAL2FLD(_CYHAL_DMAC_CH_DESCR_CTL_TR_OUT_TYPE, _cyhal_convert_output_t(output));
1006 #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
1007 SCB_CleanDCache_by_Addr((void *)&(obj->descriptor), sizeof(obj->descriptor));
1008 #endif /* defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) */
1009
1010 _cyhal_dmac_channel_enable(_cyhal_dma_dmac_get_base(obj->resource.block_num), obj->resource.channel_num);
1011 *source = _cyhal_dma_dmac_get_src(obj->resource.block_num, obj->resource.channel_num);
1012
1013 return CY_RSLT_SUCCESS;
1014 }
1015
_cyhal_dma_dmac_disconnect_digital(cyhal_dma_t * obj,cyhal_source_t source,cyhal_dma_input_t input)1016 cy_rslt_t _cyhal_dma_dmac_disconnect_digital(cyhal_dma_t *obj, cyhal_source_t source, cyhal_dma_input_t input)
1017 {
1018 if(input != CYHAL_DMA_INPUT_TRIGGER_SINGLE_ELEMENT &&
1019 input != CYHAL_DMA_INPUT_TRIGGER_SINGLE_BURST &&
1020 input != CYHAL_DMA_INPUT_TRIGGER_ALL_ELEMENTS)
1021 return CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER;
1022
1023
1024 // There is no option to totally disable. Just reset to default.
1025 // NOTE: Use .interruptType since it matches the desired .triggerInType from configure(), but
1026 // is not modified by connect/disconnect functions
1027 _cyhal_dmac_channel_disable(_cyhal_dma_dmac_get_base(obj->resource.block_num), obj->resource.channel_num);
1028
1029 obj->descriptor.dmac.ctl &= ~_CYHAL_DMAC_CH_DESCR_CTL_TR_IN_TYPE_Msk;
1030 obj->descriptor.dmac.ctl |= _VAL2FLD(_CYHAL_DMAC_CH_DESCR_CTL_TR_IN_TYPE, _cyhal_dma_dmac_default_descriptor_config.interruptType);
1031 #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
1032 SCB_CleanDCache_by_Addr((void *)&(obj->descriptor), sizeof(obj->descriptor));
1033 #endif /* defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) */
1034
1035 _cyhal_dmac_channel_enable(_cyhal_dma_dmac_get_base(obj->resource.block_num), obj->resource.channel_num);
1036
1037 cyhal_dest_t dest = _cyhal_dma_dmac_get_dest(obj->resource.block_num, obj->resource.channel_num);
1038
1039 return _cyhal_disconnect_signal(source, dest);
1040 }
1041
_cyhal_dma_dmac_disable_output(cyhal_dma_t * obj,cyhal_dma_output_t output)1042 cy_rslt_t _cyhal_dma_dmac_disable_output(cyhal_dma_t *obj, cyhal_dma_output_t output)
1043 {
1044 if(output != CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_ELEMENT &&
1045 output != CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_BURST &&
1046 output != CYHAL_DMA_OUTPUT_TRIGGER_ALL_ELEMENTS)
1047 return CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER;
1048
1049 // There is no option to totally disable. Just reset to default.
1050 _cyhal_dmac_channel_disable(_cyhal_dma_dmac_get_base(obj->resource.block_num), obj->resource.channel_num);
1051
1052 obj->descriptor.dmac.ctl &= ~_CYHAL_DMAC_CH_DESCR_CTL_TR_OUT_TYPE_Msk;
1053 obj->descriptor.dmac.ctl |= _VAL2FLD(_CYHAL_DMAC_CH_DESCR_CTL_TR_OUT_TYPE, _cyhal_dma_dmac_default_descriptor_config.triggerOutType);
1054 #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
1055 SCB_CleanDCache_by_Addr((void *)&(obj->descriptor), sizeof(obj->descriptor));
1056 #endif /* defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) */
1057
1058 _cyhal_dmac_channel_enable(_cyhal_dma_dmac_get_base(obj->resource.block_num), obj->resource.channel_num);
1059
1060 return CY_RSLT_SUCCESS;
1061
1062 }
1063 #elif defined(CY_IP_M0S8CPUSSV3_DMAC)
1064
_cyhal_dma_dmac_connect_digital(cyhal_dma_t * obj,cyhal_source_t source,cyhal_dma_input_t input)1065 cy_rslt_t _cyhal_dma_dmac_connect_digital(cyhal_dma_t *obj, cyhal_source_t source, cyhal_dma_input_t input)
1066 {
1067 if((input != CYHAL_DMA_INPUT_TRIGGER_SINGLE_ELEMENT) &&
1068 (input != CYHAL_DMA_INPUT_TRIGGER_ALL_ELEMENTS))
1069 return CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER;
1070
1071 Cy_DMAC_Channel_SetCurrentDescriptor(_cyhal_dma_dmac_get_base(obj->resource.block_num), obj->resource.channel_num, obj->descriptor);
1072
1073 cyhal_dest_t dest = (cyhal_dest_t)(CYHAL_TRIGGER_CPUSS_DMAC0_TR_IN0 + obj->resource.channel_num);
1074
1075
1076 return _cyhal_connect_signal(source, dest);
1077 }
1078
1079 // M0S8 output triggers are always active. This is a noop except to return the source.
_cyhal_dma_dmac_enable_output(cyhal_dma_t * obj,cyhal_dma_output_t output,cyhal_source_t * source)1080 cy_rslt_t _cyhal_dma_dmac_enable_output(cyhal_dma_t *obj, cyhal_dma_output_t output, cyhal_source_t *source)
1081 {
1082 if((output != CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_ELEMENT) &&
1083 (output != CYHAL_DMA_OUTPUT_TRIGGER_ALL_ELEMENTS))
1084 return CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER;
1085
1086 *source = _cyhal_dma_dmac_get_src(obj->resource.block_num, obj->resource.channel_num);
1087
1088 return CY_RSLT_SUCCESS;
1089 }
1090
_cyhal_dma_dmac_disconnect_digital(cyhal_dma_t * obj,cyhal_source_t source,cyhal_dma_input_t input)1091 cy_rslt_t _cyhal_dma_dmac_disconnect_digital(cyhal_dma_t *obj, cyhal_source_t source, cyhal_dma_input_t input)
1092 {
1093 if((input != CYHAL_DMA_INPUT_TRIGGER_SINGLE_ELEMENT) &&
1094 (input != CYHAL_DMA_INPUT_TRIGGER_ALL_ELEMENTS))
1095 return CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER;
1096
1097 // Reset to default
1098 Cy_DMAC_Channel_SetCurrentDescriptor(_cyhal_dma_dmac_get_base(obj->resource.block_num), obj->resource.channel_num, obj->descriptor);
1099
1100 cyhal_dest_t dest = (cyhal_dest_t)(CYHAL_TRIGGER_CPUSS_DMAC0_TR_IN0 + obj->resource.channel_num);
1101
1102 return _cyhal_disconnect_signal(source, dest);
1103 }
1104
1105 // M0S8 output triggers are always active. This is a noop.
_cyhal_dma_dmac_disable_output(cyhal_dma_t * obj,cyhal_dma_output_t output)1106 cy_rslt_t _cyhal_dma_dmac_disable_output(cyhal_dma_t *obj, cyhal_dma_output_t output)
1107 {
1108 CY_UNUSED_PARAMETER(obj);
1109
1110 if((output != CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_ELEMENT) &&
1111 (output != CYHAL_DMA_OUTPUT_TRIGGER_ALL_ELEMENTS))
1112 return CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER;
1113
1114 return CY_RSLT_SUCCESS;
1115 }
1116
1117 #endif /* defined(CY_IP_M4CPUSS_DMAC) || defined(CY_IP_M7CPUSS_DMAC) || defined(CY_IP_MXAHBDMAC) */
1118
1119 #endif /* (_CYHAL_DRIVER_AVAILABLE_DMA_DMAC) */
1120
1121 #if defined(__cplusplus)
1122 }
1123 #endif
1124