1 /***************************************************************************//**
2 * \file cyhal_dma_dw.c
3 *
4 * \brief
5 * Implements a high level interface for interacting with the Infineon Datawire DMA.
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_dw.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_irq_impl.h"
34 #include "cyhal_triggers.h"
35 
36 #if (_CYHAL_DRIVER_AVAILABLE_DMA_DW)
37 
38 #if defined(__cplusplus)
39 extern "C" {
40 #endif
41 
42 #if (CPUSS_DW0_PRESENT==1) && (CPUSS_DW1_PRESENT==1)
43 #define _CYHAL_DMA_DW_NUM_CHANNELS (CPUSS_DW0_CH_NR + CPUSS_DW1_CH_NR)
44 #elif (CPUSS_DW0_PRESENT==1)
45 #define _CYHAL_DMA_DW_NUM_CHANNELS (CPUSS_DW0_CH_NR)
46 #endif
47 
48 // These devices do not have interrupts for all channels. Channels 29 and greater are absent.
49 #if defined(CY_DEVICE_PSOC6A256K) || defined(CY_DEVICE_PSOC6A512K)
50 #define CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ   29
51 #define CYHAL_DMA_DW1_MAX_CONTIGUOUS_IRQ   29
52 #else
53 #define CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ   CPUSS_DW0_CH_NR
54 #define CYHAL_DMA_DW1_MAX_CONTIGUOUS_IRQ   CPUSS_DW1_CH_NR
55 #endif
56 
57 #define _CYHAL_DMA_GET_CPUSS_IRQN_IMPL(block, channel)   cpuss_interrupts_dw##block##_##channel##_IRQn
58 #define _CYHAL_DMA_GET_CPUSS_IRQN(block, channel)        _CYHAL_DMA_GET_CPUSS_IRQN_IMPL(block, channel)
59 
60 static cyhal_dma_t* _cyhal_dma_dw_config_structs[_CYHAL_DMA_DW_NUM_CHANNELS];
61 
62 /** Default dw descriptor config */
63 static const cy_stc_dma_descriptor_config_t _cyhal_dma_dw_default_descriptor_config =
64 {
65     .retrigger = CY_DMA_RETRIG_IM,
66     .interruptType = CY_DMA_DESCR,                  // Overridden by cyhal_dma_cfg_t.action
67     .triggerOutType = CY_DMA_DESCR_CHAIN,           // Overridden by [en/dis]able_output()
68     .channelState = CY_DMA_CHANNEL_ENABLED,         // Overridden by cyhal_dma_cfg_t.action
69     .triggerInType = CY_DMA_DESCR,                  // Overridden by cyhal_dma_cfg_t.action & [dis]connect_digital()
70     .dataSize = CY_DMA_WORD,                        // Overridden by cyhal_dma_cfg_t.transfer_width
71     .srcTransferSize = CY_DMA_TRANSFER_SIZE_DATA,   // Overriden by direction
72     .dstTransferSize = CY_DMA_TRANSFER_SIZE_DATA,   // Overriden by direction
73     .descriptorType = CY_DMA_1D_TRANSFER,           // Overriden by cyhal_dma_cfg_t.burst_size
74     .srcAddress = 0,                                // Overriden by cyhal_dma_cfg_t.src_addr
75     .dstAddress = 0,                                // Overriden by cyhal_dma_cfg_t.dst_addr
76     .srcXincrement = 1U,                            // Overriden by cyhal_dma_cfg_t.src_increment
77     .dstXincrement = 1U,                            // Overriden by cyhal_dma_cfg_t.dst_increment
78     .xCount = 1UL,                                  // Overriden by cyhal_dma_cfg_t.length/burst_size
79     .srcYincrement = 0U,                            // Overriden by cyhal_dma_cfg_t.burst_size
80     .dstYincrement = 0U,                            // Overriden by cyhal_dma_cfg_t.burst_size
81     .yCount = 1UL,                                  // Overriden by cyhal_dma_cfg_t.length
82     .nextDescriptor = 0,
83 };
84 
85 /** Default dw channel config */
86 static const cy_stc_dma_channel_config_t _cyhal_dma_dw_default_channel_config =
87 {
88     .descriptor = 0,        // Overriden by config()
89     .preemptable = false,
90     .priority = 1,          // Overriden by config().priority
91     .enable = false,
92     .bufferable = false,
93 };
94 
95 static bool _cyhal_dma_dw_pm_transition_pending = false;
96 
97 #if CYHAL_DRIVER_AVAILABLE_SYSPM
98 static bool _cyhal_dma_dw_pm_callback(cyhal_syspm_callback_state_t state, cyhal_syspm_callback_mode_t mode, void* callback_arg);
99 
100 static cyhal_syspm_callback_data_t cyhal_dma_dw_pm_callback_args = {
101     .callback = &_cyhal_dma_dw_pm_callback,
102     .states = (cyhal_syspm_callback_state_t)(CYHAL_SYSPM_CB_CPU_DEEPSLEEP | CYHAL_SYSPM_CB_CPU_DEEPSLEEP_RAM | CYHAL_SYSPM_CB_SYSTEM_HIBERNATE),
103     .next = NULL,
104     .args = NULL,
105     .ignore_modes = (cyhal_syspm_callback_mode_t)(CYHAL_SYSPM_BEFORE_TRANSITION | CYHAL_SYSPM_AFTER_DS_WFI_TRANSITION),
106 };
107 
_cyhal_dma_dw_has_enabled(void)108 static bool _cyhal_dma_dw_has_enabled(void)
109 {
110     for (uint8_t i = 0; i < _CYHAL_DMA_DW_NUM_CHANNELS; i++)
111         if (NULL != _cyhal_dma_dw_config_structs[i])
112             return true;
113     return false;
114 }
115 
_cyhal_dma_dw_pm_callback(cyhal_syspm_callback_state_t state,cyhal_syspm_callback_mode_t mode,void * callback_arg)116 static bool _cyhal_dma_dw_pm_callback(cyhal_syspm_callback_state_t state, cyhal_syspm_callback_mode_t mode, void* callback_arg)
117 {
118     CY_UNUSED_PARAMETER(state);
119     CY_UNUSED_PARAMETER(callback_arg);
120     bool block_transition = false;
121     switch(mode)
122     {
123         case CYHAL_SYSPM_CHECK_READY:
124             for (uint8_t i = 0; i < _CYHAL_DMA_DW_NUM_CHANNELS && !block_transition; i++)
125             {
126                 block_transition |= (_cyhal_dma_dw_config_structs[i] != NULL) && _cyhal_dma_dw_is_busy(_cyhal_dma_dw_config_structs[i]);
127             }
128 
129             _cyhal_dma_dw_pm_transition_pending = !block_transition;
130             break;
131 
132         case CYHAL_SYSPM_CHECK_FAIL:
133         case CYHAL_SYSPM_AFTER_TRANSITION:
134             _cyhal_dma_dw_pm_transition_pending = false;
135             break;
136         default:
137             CY_ASSERT(false);
138             break;
139     }
140     return _cyhal_dma_dw_pm_transition_pending;
141 }
142 #endif
143 
144 /** Sets the dw configuration struct */
_cyhal_dma_dw_set_obj(cyhal_dma_t * obj)145 static inline void _cyhal_dma_dw_set_obj(cyhal_dma_t *obj)
146 {
147     _cyhal_dma_dw_config_structs[obj->resource.block_num * CPUSS_DW0_CH_NR + obj->resource.channel_num] = obj;
148 }
149 
150 /** Zeros the dw configuration struct */
_cyhal_dma_dw_free_obj(cyhal_dma_t * obj)151 static inline void _cyhal_dma_dw_free_obj(cyhal_dma_t *obj)
152 {
153     _cyhal_dma_dw_config_structs[obj->resource.block_num * CPUSS_DW0_CH_NR + obj->resource.channel_num] = NULL;
154 }
155 
156 /** Gets the dw configuration struct from block and channel */
_cyhal_dma_dw_get_obj(uint8_t block,uint8_t channel)157 static inline cyhal_dma_t* _cyhal_dma_dw_get_obj(uint8_t block, uint8_t channel)
158 {
159     return _cyhal_dma_dw_config_structs[block * CPUSS_DW0_CH_NR + channel];
160 }
161 
162 /** Gets the dw block number from irq number */
163 /** This should never be called from a non-dma IRQn */
_cyhal_dma_dw_get_block_from_irqn(_cyhal_system_irq_t irqn)164 static inline uint8_t _cyhal_dma_dw_get_block_from_irqn(_cyhal_system_irq_t irqn)
165 {
166 #if (CPUSS_DW0_PRESENT==1)
167 #if defined(COMPONENT_CAT1D)
168     if (irqn >= m33syscpuss_interrupts_dw0_0_IRQn && irqn < m33syscpuss_interrupts_dw0_0_IRQn + (_cyhal_system_irq_t)CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ)
169 #else
170     if (irqn >= cpuss_interrupts_dw0_0_IRQn && irqn < cpuss_interrupts_dw0_0_IRQn + (_cyhal_system_irq_t)CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ)
171 #endif
172         return 0;
173 #if (CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ != CPUSS_DW0_CH_NR)
174     if ((irqn >= _CYHAL_DMA_GET_CPUSS_IRQN(0, CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ)) &&
175         (irqn < (_cyhal_system_irq_t)(_CYHAL_DMA_GET_CPUSS_IRQN(0, CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ) + CPUSS_DW0_CH_NR - CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ)))
176         return 0;
177 #endif
178 #endif
179 #if (CPUSS_DW1_PRESENT==1)
180     if (irqn >= cpuss_interrupts_dw1_0_IRQn && irqn < cpuss_interrupts_dw1_0_IRQn + (_cyhal_system_irq_t)CYHAL_DMA_DW1_MAX_CONTIGUOUS_IRQ)
181         return 1;
182 #if (CYHAL_DMA_DW1_MAX_CONTIGUOUS_IRQ != CPUSS_DW1_CH_NR)
183     if ((irqn >= _CYHAL_DMA_GET_CPUSS_IRQN(1, CYHAL_DMA_DW1_MAX_CONTIGUOUS_IRQ)) &&
184         (irqn < (_cyhal_system_irq_t)(_CYHAL_DMA_GET_CPUSS_IRQN(1, CYHAL_DMA_DW1_MAX_CONTIGUOUS_IRQ) + CPUSS_DW1_CH_NR - CYHAL_DMA_DW1_MAX_CONTIGUOUS_IRQ)))
185         return 1;
186 #endif
187 #endif
188     CY_ASSERT(false);
189     return 0xFF;
190 }
191 
192 /** Gets the dw channel number from irq number */
193 /** This should never be called from a non-dma IRQn */
_cyhal_dma_dw_get_channel_from_irqn(_cyhal_system_irq_t irqn)194 static inline uint8_t _cyhal_dma_dw_get_channel_from_irqn(_cyhal_system_irq_t irqn)
195 {
196 #if (CPUSS_DW0_PRESENT==1)
197 #if defined(COMPONENT_CAT1D)
198     if (irqn >= m33syscpuss_interrupts_dw0_0_IRQn && irqn < m33syscpuss_interrupts_dw0_0_IRQn + (_cyhal_system_irq_t)CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ)
199         return (uint8_t)(irqn - m33syscpuss_interrupts_dw0_0_IRQn);
200 #else
201     if (irqn >= cpuss_interrupts_dw0_0_IRQn && irqn < cpuss_interrupts_dw0_0_IRQn + (_cyhal_system_irq_t)CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ)
202         return (uint8_t)(irqn - cpuss_interrupts_dw0_0_IRQn);
203 #endif
204 #if (CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ != CPUSS_DW0_CH_NR)
205     if ((irqn >= _CYHAL_DMA_GET_CPUSS_IRQN(0, CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ)) &&
206         (irqn < (_cyhal_system_irq_t)(_CYHAL_DMA_GET_CPUSS_IRQN(0, CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ) + CPUSS_DW0_CH_NR - CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ)))
207         return CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ + irqn - _CYHAL_DMA_GET_CPUSS_IRQN(0, CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ);
208 #endif
209 #endif
210 #if (CPUSS_DW1_PRESENT==1)
211     if (irqn >= cpuss_interrupts_dw1_0_IRQn && irqn < cpuss_interrupts_dw1_0_IRQn + (_cyhal_system_irq_t)CYHAL_DMA_DW1_MAX_CONTIGUOUS_IRQ)
212         return (uint8_t)(irqn - cpuss_interrupts_dw1_0_IRQn);
213 #if (CYHAL_DMA_DW1_MAX_CONTIGUOUS_IRQ != CPUSS_DW1_CH_NR)
214     if ((irqn >= _CYHAL_DMA_GET_CPUSS_IRQN(1, CYHAL_DMA_DW1_MAX_CONTIGUOUS_IRQ)) &&
215         (irqn < (_cyhal_system_irq_t)(_CYHAL_DMA_GET_CPUSS_IRQN(1, CYHAL_DMA_DW1_MAX_CONTIGUOUS_IRQ) + CPUSS_DW1_CH_NR - CYHAL_DMA_DW1_MAX_CONTIGUOUS_IRQ)))
216         return CYHAL_DMA_DW1_MAX_CONTIGUOUS_IRQ + irqn - _CYHAL_DMA_GET_CPUSS_IRQN(1, CYHAL_DMA_DW1_MAX_CONTIGUOUS_IRQ);
217 #endif
218 #endif
219     CY_ASSERT(false);
220     return 0xFF;
221 }
222 
223 /** Gets the irqn corresponding to a particular cyhal_dma_t config struct */
_cyhal_dma_dw_get_irqn(cyhal_dma_t * obj)224 static inline _cyhal_system_irq_t _cyhal_dma_dw_get_irqn(cyhal_dma_t *obj)
225 {
226     // Some devices do not have contigious interrupts for all channels.
227 #if (CPUSS_DW0_PRESENT==1)
228     if (obj->resource.block_num == 0 && obj->resource.channel_num < CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ)
229     {
230 #if defined(COMPONENT_CAT1D)
231         return (_cyhal_system_irq_t)((uint8_t)m33syscpuss_interrupts_dw0_0_IRQn + obj->resource.channel_num);
232 #else
233         return (_cyhal_system_irq_t)((uint8_t)cpuss_interrupts_dw0_0_IRQn + obj->resource.channel_num);
234 #endif
235     }
236     #if (CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ != CPUSS_DW0_CH_NR)
237     else if ((obj->resource.block_num == 0 && obj->resource.channel_num < CPUSS_DW0_CH_NR))
238     {
239         return (_cyhal_system_irq_t)((uint8_t)_CYHAL_DMA_GET_CPUSS_IRQN(0, CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ) + (obj->resource.channel_num - CYHAL_DMA_DW0_MAX_CONTIGUOUS_IRQ));
240     }
241     #endif
242 #endif
243 #if (CPUSS_DW1_PRESENT==1)
244     if (obj->resource.block_num == 1 && obj->resource.channel_num < CYHAL_DMA_DW1_MAX_CONTIGUOUS_IRQ)
245     {
246         return (_cyhal_system_irq_t)((uint16_t)cpuss_interrupts_dw1_0_IRQn + obj->resource.channel_num);
247     }
248     #if (CYHAL_DMA_DW1_MAX_CONTIGUOUS_IRQ != CPUSS_DW1_CH_NR)
249     else if (obj->resource.block_num == 1 && obj->resource.channel_num < CPUSS_DW1_CH_NR)
250     {
251         return (_cyhal_system_irq_t)((uint8_t)_CYHAL_DMA_GET_CPUSS_IRQN(1, CYHAL_DMA_DW1_MAX_CONTIGUOUS_IRQ) + (obj->resource.channel_num - CYHAL_DMA_DW1_MAX_CONTIGUOUS_IRQ));
252     }
253     #endif
254 #endif
255 
256     CY_ASSERT(false);
257 #if (_CYHAL_IRQ_MUXING)
258     return disconnected_IRQn;
259 #else
260     return unconnected_IRQn;
261 #endif
262 }
263 
264 /** Gets the dw base pointer from block number */
_cyhal_dma_dw_get_base(uint8_t block_num)265 static inline DW_Type* _cyhal_dma_dw_get_base(uint8_t block_num)
266 {
267 #if (CPUSS_DW1_PRESENT==1)
268     return block_num == 0 ? DW0 : DW1;
269 #else
270     CY_UNUSED_PARAMETER(block_num);
271     return DW0;
272 #endif
273 }
274 
275 /** Uses tables provided as part of the hal interconnect driver to determine mux
276  * trigger group and mux trigger index and then construct the trigger line
277  * input parameter to Cy_TrigMux_SwTrigger. */
_cyhal_dma_dw_get_trigger_line(uint8_t block_num,uint8_t channel_num)278 static inline uint32_t _cyhal_dma_dw_get_trigger_line(uint8_t block_num, uint8_t channel_num)
279 {
280     /* cyhal_dest_t triggers are guaranteed to be sorted by trigger type, block
281      * num, then channel num, therefore, we can just directly find the proper
282      * trigger by calculating an offset. */
283 #if defined(COMPONENT_CAT1D)
284     cyhal_dest_t trigger = (cyhal_dest_t)(CYHAL_TRIGGER_M33SYSCPUSS_DW0_TR_IN0 + (block_num * CPUSS_DW0_CH_NR) + channel_num);
285 #else
286     cyhal_dest_t trigger = (cyhal_dest_t)(CYHAL_TRIGGER_CPUSS_DW0_TR_IN0 + (block_num * CPUSS_DW0_CH_NR) + channel_num);
287 #endif
288 
289     /* One to one triggers have bit 8 set in cyhal_dest_to_mux but
290      * Cy_TrigMux_SwTrigger wants the trigger group field to have bit 5 set to
291      * denote one to one triggers. */
292     uint8_t trig_group = cyhal_dest_to_mux[trigger];
293     /* If hal one to one triggers bit is set: mask it out and set pdl one to
294      * one bit */
295     if(trig_group & _CYHAL_DMA_TRIGGERS_1TO1_MASK)
296         trig_group = (trig_group & ~_CYHAL_DMA_TRIGGERS_1TO1_MASK) | _CYHAL_DMA_PDL_TRIGGERS_1TO1_MASK;
297 
298     /* Construct trigger line which consists of three fields packed into a
299      * uint32_t:
300      * Bits   30: Input/output bit. Set to 1 for output.
301      * Bits 12-8: Trigger group selection.
302      * Bits  7-0: Select the output trigger number in the trigger group. */
303     return PERI_TR_CMD_OUT_SEL_Msk | (uint32_t)((uint32_t)trig_group << 8) | cyhal_mux_dest_index[trigger];
304 }
305 
306 /** Convert PDL interrupt cause to hal dma event */
_cyhal_dma_dw_convert_interrupt_cause(cyhal_dma_t * obj,cy_en_dma_intr_cause_t cause)307 static inline cyhal_dma_event_t _cyhal_dma_dw_convert_interrupt_cause(cyhal_dma_t *obj, cy_en_dma_intr_cause_t cause)
308 {
309     static const cyhal_dma_event_t hal[] =
310     {
311         CYHAL_DMA_NO_INTR,
312         CYHAL_DMA_TRANSFER_COMPLETE,
313         CYHAL_DMA_SRC_BUS_ERROR,
314         CYHAL_DMA_DST_BUS_ERROR,
315         CYHAL_DMA_SRC_MISAL,
316         CYHAL_DMA_DST_MISAL,
317         CYHAL_DMA_CURR_PTR_NULL,
318         CYHAL_DMA_ACTIVE_CH_DISABLED,
319         CYHAL_DMA_DESCR_BUS_ERROR
320     };
321 
322     cyhal_dma_event_t hal_rslt = CYHAL_DMA_NO_INTR;
323     CY_ASSERT(cause < sizeof(hal)/sizeof(cyhal_dma_event_t));
324     if (cause < sizeof(hal)/sizeof(cyhal_dma_event_t))
325         hal_rslt = hal[cause];
326 
327     if ((uint32_t)(hal_rslt & CYHAL_DMA_TRANSFER_COMPLETE) > 0 && obj->expected_bursts > 0)
328     {
329         obj->expected_bursts--;
330         if (0 == obj->expected_bursts)
331         {
332             hal_rslt |= CYHAL_DMA_DESCRIPTOR_COMPLETE;
333             obj->expected_bursts = (obj->descriptor_config.dw.interruptType == CY_DMA_X_LOOP)
334                 ? obj->descriptor_config.dw.yCount
335                 : 1;
336         }
337     }
338 
339     return hal_rslt;
340 }
341 
342 /** DW irq handler */
_cyhal_dma_dw_irq_handler(void)343 static void _cyhal_dma_dw_irq_handler(void)
344 {
345     /* Use irqn to get appropriate config structure */
346     _cyhal_system_irq_t active_irq = _cyhal_irq_get_active();
347     uint8_t block = _cyhal_dma_dw_get_block_from_irqn(active_irq);
348     uint8_t channel = _cyhal_dma_dw_get_channel_from_irqn(active_irq);
349     cyhal_dma_t *obj = _cyhal_dma_dw_get_obj(block, channel);
350 
351     /* Get interrupt type and call users event callback if they have enabled that event */
352     cy_en_dma_intr_cause_t cause = Cy_DMA_Channel_GetStatus(_cyhal_dma_dw_get_base(block), channel);
353     cyhal_dma_event_t event_type = _cyhal_dma_dw_convert_interrupt_cause(obj, cause);
354     uint32_t events_to_callback = event_type & obj->irq_cause;
355     if(obj->callback_data.callback != NULL && events_to_callback)
356     {
357         ((cyhal_dma_event_callback_t)obj->callback_data.callback)(obj->callback_data.callback_arg, (cyhal_dma_event_t)events_to_callback);
358     }
359 
360     /* Clear all interrupts */
361     Cy_DMA_Channel_ClearInterrupt(_cyhal_dma_dw_get_base(block), channel);
362 }
363 
_cyhal_dma_dw_get_src(uint8_t block_num,uint8_t channel_num)364 static cyhal_source_t _cyhal_dma_dw_get_src(uint8_t block_num, uint8_t channel_num)
365 {
366 #if defined(COMPONENT_CAT1D)
367     return (cyhal_source_t)_CYHAL_TRIGGER_CREATE_SOURCE(_CYHAL_TRIGGER_M33SYSCPUSS_DW0_TR_OUT0 + (block_num * CPUSS_DW0_CH_NR) + channel_num, CYHAL_SIGNAL_TYPE_EDGE);
368 #else
369     return (cyhal_source_t)_CYHAL_TRIGGER_CREATE_SOURCE(_CYHAL_TRIGGER_CPUSS_DW0_TR_OUT0 + (block_num * CPUSS_DW0_CH_NR) + channel_num, CYHAL_SIGNAL_TYPE_EDGE);
370 #endif
371 }
372 
_cyhal_dma_dw_get_dest(uint8_t block_num,uint8_t channel_num)373 static cyhal_dest_t _cyhal_dma_dw_get_dest(uint8_t block_num, uint8_t channel_num)
374 {
375 #if defined(COMPONENT_CAT1D)
376     return (cyhal_dest_t)(CYHAL_TRIGGER_M33SYSCPUSS_DW0_TR_IN0 + (block_num * CPUSS_DW0_CH_NR) + channel_num);
377 #else
378     return (cyhal_dest_t)(CYHAL_TRIGGER_CPUSS_DW0_TR_IN0 + (block_num * CPUSS_DW0_CH_NR) + channel_num);
379 #endif
380 }
381 
_cyhal_dma_dw_stage(cyhal_dma_t * obj)382 cy_rslt_t _cyhal_dma_dw_stage(cyhal_dma_t *obj)
383 {
384     if(CY_DMA_SUCCESS != Cy_DMA_Descriptor_Init(&obj->descriptor.dw, &obj->descriptor_config.dw))
385         return CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER;
386 
387     /* Setup channel and enable */
388     DW_Type* base = _cyhal_dma_dw_get_base(obj->resource.block_num);
389     if(CY_DMA_SUCCESS != Cy_DMA_Channel_Init(base, obj->resource.channel_num, &obj->channel_config.dw))
390         return CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER;
391 
392     Cy_DMA_Channel_SetInterruptMask (base, obj->resource.channel_num, CY_DMA_INTR_MASK);
393 
394     Cy_DMA_Enable(base);
395 
396     /* src_misal and dst_misal interrupts are triggered immediately on enable
397      * so return those errors here */
398     uint32_t status = Cy_DMA_Channel_GetInterruptStatus(base, obj->resource.channel_num);
399     if((status == CY_DMA_INTR_CAUSE_SRC_MISAL) ||
400        (status == CY_DMA_INTR_CAUSE_DST_MISAL))
401     {
402         Cy_DMA_Channel_ClearInterrupt(base, obj->resource.channel_num);
403         return CYHAL_DMA_RSLT_ERR_INVALID_ALIGNMENT;
404     }
405 
406     /* Enable interrupt for this channel; preserve user priority if they enabled an interrupt */
407     _cyhal_system_irq_t irqn = _cyhal_dma_dw_get_irqn(obj);
408     uint32_t priority = (CYHAL_DMA_NO_INTR == obj->irq_cause)
409         ? CYHAL_ISR_PRIORITY_DEFAULT
410         : _cyhal_irq_get_priority(irqn);
411 
412     if(CY_RSLT_SUCCESS != _cyhal_irq_register(irqn, priority, _cyhal_dma_dw_irq_handler))
413         return CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER;
414     _cyhal_irq_enable(irqn);
415 
416     #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
417     SCB_CleanDCache_by_Addr((void *)&(obj->descriptor), sizeof(obj->descriptor));
418     #endif /* defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) */
419 
420     return CY_RSLT_SUCCESS;
421 }
422 
_cyhal_dma_dw_init(cyhal_dma_t * obj,cyhal_source_t * src,cyhal_dest_t * dest,uint8_t priority)423 cy_rslt_t _cyhal_dma_dw_init(cyhal_dma_t *obj, cyhal_source_t *src, cyhal_dest_t *dest, uint8_t priority)
424 {
425     if(!CY_DMA_IS_PRIORITY_VALID(priority))
426         return CYHAL_DMA_RSLT_ERR_INVALID_PRIORITY;
427 
428     cy_rslt_t rslt = _cyhal_hwmgr_allocate_with_connection(
429         CYHAL_RSC_DW, src, dest, _cyhal_dma_dw_get_src, _cyhal_dma_dw_get_dest, &obj->resource);
430     if(rslt != CY_RSLT_SUCCESS)
431         return rslt;
432 
433     /* Setup descriptor and channel configs */
434     obj->descriptor_config.dw = _cyhal_dma_dw_default_descriptor_config;
435     obj->channel_config.dw = _cyhal_dma_dw_default_channel_config;
436     obj->channel_config.dw.descriptor = &obj->descriptor.dw;
437     obj->channel_config.dw.priority = priority;
438 #if CYHAL_DRIVER_AVAILABLE_SYSPM
439     if (!_cyhal_dma_dw_has_enabled())
440     {
441         _cyhal_syspm_register_peripheral_callback(&cyhal_dma_dw_pm_callback_args);
442     }
443 #endif
444     _cyhal_dma_dw_set_obj(obj);
445 
446     return CY_RSLT_SUCCESS;
447 }
448 
_cyhal_dma_dw_init_cfg(cyhal_dma_t * obj,const cyhal_dma_configurator_t * cfg)449 cy_rslt_t _cyhal_dma_dw_init_cfg(cyhal_dma_t *obj, const cyhal_dma_configurator_t *cfg)
450 {
451     if (_cyhal_dma_dw_pm_transition_pending)
452     {
453         return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
454     }
455 
456     obj->resource = *(cfg->resource);
457     obj->callback_data.callback = NULL;
458 
459     /* Setup descriptor and channel configs */
460     obj->descriptor_config.dw = *(cfg->dw_descriptor_config);
461     obj->channel_config.dw = *(cfg->dw_channel_config);
462     obj->channel_config.dw.descriptor = &obj->descriptor.dw;
463     obj->expected_bursts = cfg->dw_descriptor_config->yCount;
464 
465 #if CYHAL_DRIVER_AVAILABLE_SYSPM
466     if (!_cyhal_dma_dw_has_enabled())
467     {
468         _cyhal_syspm_register_peripheral_callback(&cyhal_dma_dw_pm_callback_args);
469     }
470 #endif
471 
472     _cyhal_dma_dw_set_obj(obj);
473 
474     return _cyhal_dma_dw_stage(obj);
475 }
476 
_cyhal_dma_dw_free(cyhal_dma_t * obj)477 void _cyhal_dma_dw_free(cyhal_dma_t *obj)
478 {
479     Cy_DMA_Descriptor_DeInit(&obj->descriptor.dw);
480     Cy_DMA_Channel_DeInit(_cyhal_dma_dw_get_base(obj->resource.block_num), obj->resource.channel_num);
481 
482     _cyhal_irq_free(_cyhal_dma_dw_get_irqn(obj));
483 
484     _cyhal_dma_dw_free_obj(obj);
485 #if CYHAL_DRIVER_AVAILABLE_SYSPM
486     if (!_cyhal_dma_dw_has_enabled())
487     {
488         _cyhal_syspm_unregister_peripheral_callback(&cyhal_dma_dw_pm_callback_args);
489         _cyhal_dma_dw_pm_transition_pending = false;
490     }
491 #endif
492 }
493 
494 /* Initialize descriptor, initialize channel, enable channel, enable channel
495  * interrupt, and enable DW controller */
_cyhal_dma_dw_configure(cyhal_dma_t * obj,const cyhal_dma_cfg_t * cfg)496 cy_rslt_t _cyhal_dma_dw_configure(cyhal_dma_t *obj, const cyhal_dma_cfg_t *cfg)
497 {
498     /* Do not reconfigure if transfer is pending/active already */
499     if(_cyhal_dma_dw_is_busy(obj))
500         return CYHAL_DMA_RSLT_ERR_CHANNEL_BUSY;
501 
502     // DataWire only supports <=256 byte burst and <=256 bytes per burst
503     if ((cfg->burst_size > 256) ||
504         (cfg->burst_size <= 1 && cfg->length > 256) ||
505         (cfg->burst_size > 0 && cfg->length > (cfg->burst_size * 256)))
506         return CYHAL_DMA_RSLT_ERR_INVALID_TRANSFER_SIZE;
507 
508     obj->descriptor_config.dw.srcAddress = (void*)cfg->src_addr;
509     obj->descriptor_config.dw.dstAddress = (void*)cfg->dst_addr;
510     obj->descriptor_config.dw.nextDescriptor = &obj->descriptor.dw;
511     if (cfg->action == CYHAL_DMA_TRANSFER_BURST || cfg->action == CYHAL_DMA_TRANSFER_FULL)
512     {
513         obj->descriptor_config.dw.channelState = CY_DMA_CHANNEL_ENABLED;
514     }
515     else
516     {
517         obj->descriptor_config.dw.channelState = CY_DMA_CHANNEL_DISABLED;
518     }
519 
520     if(cfg->transfer_width == 8)
521         obj->descriptor_config.dw.dataSize = CY_DMA_BYTE;
522     else if(cfg->transfer_width == 16)
523         obj->descriptor_config.dw.dataSize = CY_DMA_HALFWORD;
524     else if(cfg->transfer_width == 32)
525         obj->descriptor_config.dw.dataSize = CY_DMA_WORD;
526     else
527         return CYHAL_DMA_RSLT_ERR_INVALID_TRANSFER_WIDTH;
528 
529     /* By default, transfer what the user set for dataSize. However, if transfering between memory
530      * and a peripheral, make sure the peripheral access is using words. */
531     obj->descriptor_config.dw.srcTransferSize =
532         obj->descriptor_config.dw.dstTransferSize = CY_DMA_TRANSFER_SIZE_DATA;
533     if (obj->direction == CYHAL_DMA_DIRECTION_PERIPH2MEM)
534         obj->descriptor_config.dw.srcTransferSize = CY_DMA_TRANSFER_SIZE_WORD;
535     else if (obj->direction == CYHAL_DMA_DIRECTION_MEM2PERIPH)
536         obj->descriptor_config.dw.dstTransferSize = CY_DMA_TRANSFER_SIZE_WORD;
537 
538     /* Length must be a multiple of burst_size */
539     if(cfg->burst_size != 0 && cfg->length % cfg->burst_size != 0)
540         return CYHAL_DMA_RSLT_ERR_INVALID_BURST_SIZE;
541 
542     /* Setup 2D transfer if burst_size is being used otherwise set up 1D transfer */
543     obj->descriptor_config.dw.srcXincrement = cfg->src_increment;
544     obj->descriptor_config.dw.dstXincrement = cfg->dst_increment;
545 
546     if(cfg->burst_size != 0)
547     {
548         obj->descriptor_config.dw.descriptorType = CY_DMA_2D_TRANSFER;
549         obj->descriptor_config.dw.xCount = cfg->burst_size;
550         obj->descriptor_config.dw.yCount = cfg->length / cfg->burst_size;
551         obj->descriptor_config.dw.srcYincrement = cfg->src_increment * (int32_t)cfg->burst_size;
552         obj->descriptor_config.dw.dstYincrement = cfg->dst_increment * (int32_t)cfg->burst_size;
553     }
554     else
555     {
556         obj->descriptor_config.dw.descriptorType = CY_DMA_1D_TRANSFER;
557         obj->descriptor_config.dw.xCount = cfg->length;
558         obj->descriptor_config.dw.yCount = 1;
559         obj->descriptor_config.dw.srcYincrement = 0;
560         obj->descriptor_config.dw.dstYincrement = 0;
561     }
562 
563     /* If burst action, configure trigger and interrupt actions */
564     if (cfg->burst_size != 0 &&
565         (cfg->action == CYHAL_DMA_TRANSFER_BURST || cfg->action == CYHAL_DMA_TRANSFER_BURST_DISABLE))
566     {
567         obj->expected_bursts = obj->descriptor_config.dw.yCount;
568         obj->descriptor_config.dw.interruptType = CY_DMA_X_LOOP;
569         if (obj->source == CYHAL_TRIGGER_CPUSS_ZERO) // If not overridden by connect_digital()
570             obj->descriptor_config.dw.triggerInType = CY_DMA_X_LOOP;
571     }
572     else
573     {
574         obj->expected_bursts = 1;
575         obj->descriptor_config.dw.interruptType = CY_DMA_DESCR;
576         if (obj->source == CYHAL_TRIGGER_CPUSS_ZERO) // If not overridden by connect_digital()
577             obj->descriptor_config.dw.triggerInType = CY_DMA_DESCR;
578     }
579 
580     return _cyhal_dma_dw_stage(obj);
581 }
582 
_cyhal_dma_dw_enable(cyhal_dma_t * obj)583 cy_rslt_t _cyhal_dma_dw_enable(cyhal_dma_t *obj)
584 {
585     DW_Type* base = _cyhal_dma_dw_get_base(obj->resource.block_num);
586     Cy_DMA_Channel_Enable(base, obj->resource.channel_num);
587     return CY_RSLT_SUCCESS;
588 }
589 
_cyhal_dma_dw_disable(cyhal_dma_t * obj)590 cy_rslt_t _cyhal_dma_dw_disable(cyhal_dma_t *obj)
591 {
592     DW_Type* base = _cyhal_dma_dw_get_base(obj->resource.block_num);
593     Cy_DMA_Channel_Disable(base, obj->resource.channel_num);
594     return CY_RSLT_SUCCESS;
595 }
596 
_cyhal_dma_dw_start_transfer(cyhal_dma_t * obj)597 cy_rslt_t _cyhal_dma_dw_start_transfer(cyhal_dma_t *obj)
598 {
599     /* Return warning if channel is busy */
600     if(_cyhal_dma_dw_is_busy(obj))
601         return CYHAL_DMA_RSLT_WARN_TRANSFER_ALREADY_STARTED;
602 
603     if (_cyhal_dma_dw_pm_transition_pending)
604         return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
605 
606     uint32_t trigline = _cyhal_dma_dw_get_trigger_line(obj->resource.block_num, obj->resource.channel_num);
607     cy_en_trigmux_status_t trig_status = Cy_TrigMux_SwTrigger(trigline, CY_TRIGGER_TWO_CYCLES);
608 
609     /* Also return warning if SW trigger is already initiated but DMA hardware
610      * has not seen it yet */
611     if(trig_status == CY_TRIGMUX_INVALID_STATE)
612         return CYHAL_DMA_RSLT_WARN_TRANSFER_ALREADY_STARTED;
613     else
614         return CY_RSLT_SUCCESS;
615 }
616 
_cyhal_dma_dw_enable_event(cyhal_dma_t * obj,cyhal_dma_event_t event,uint8_t intr_priority,bool enable)617 void _cyhal_dma_dw_enable_event(cyhal_dma_t *obj, cyhal_dma_event_t event, uint8_t intr_priority, bool enable)
618 {
619     if(enable)
620         obj->irq_cause |= event;
621     else
622         obj->irq_cause &= ~event;
623 
624     _cyhal_irq_set_priority(_cyhal_dma_dw_get_irqn(obj), intr_priority);
625 }
626 
_cyhal_dma_dw_is_busy(cyhal_dma_t * obj)627 bool _cyhal_dma_dw_is_busy(cyhal_dma_t *obj)
628 {
629 #if CY_IP_M4CPUSS_DMA_VERSION == 1
630     /* In DW_V1 the pending channel information is stored in the PENDING
631      * register of the DW block and is a bit field of all pending or active
632      * channels */
633     return _cyhal_dma_dw_get_base(obj->resource.block_num)->PENDING & (1 << obj->resource.channel_num);
634 #elif (CY_IP_M4CPUSS_DMA_VERSION == 2)
635     /* In DW_V2 the pending channel information is stored in the STATUS
636      * register of the channel itself */
637     return DW_CH_STATUS(_cyhal_dma_dw_get_base(obj->resource.block_num), obj->resource.channel_num) & (1UL << DW_CH_STRUCT_V2_CH_STATUS_PENDING_Pos);
638 #elif defined(CY_IP_M7CPUSS) || defined(CY_IP_MXDW)
639     return DW_CH_STATUS(_cyhal_dma_dw_get_base(obj->resource.block_num), obj->resource.channel_num) & (1UL << DW_CH_STRUCT_CH_STATUS_PENDING_Pos);
640 #else
641     // Should never reach here. Just silencing compiler warnings.
642     CY_ASSERT(false);
643     return false;
644 #endif
645 }
646 
_cyhal_convert_input_t(cyhal_dma_input_t input)647 static cy_en_dma_trigger_type_t _cyhal_convert_input_t(cyhal_dma_input_t input)
648 {
649     switch(input)
650     {
651         case CYHAL_DMA_INPUT_TRIGGER_SINGLE_ELEMENT:
652             return CY_DMA_1ELEMENT;
653         case CYHAL_DMA_INPUT_TRIGGER_SINGLE_BURST:
654             return CY_DMA_X_LOOP;
655         case CYHAL_DMA_INPUT_TRIGGER_ALL_ELEMENTS:
656             return CY_DMA_DESCR;
657         default:
658             // Should never reach here. Just silencing compiler warnings.
659             CY_ASSERT(false);
660             return CY_DMA_DESCR;
661     }
662 }
663 
_cyhal_convert_output_t(cyhal_dma_output_t output)664 static cy_en_dma_trigger_type_t _cyhal_convert_output_t(cyhal_dma_output_t output)
665 {
666     switch(output)
667     {
668         case CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_ELEMENT:
669             return CY_DMA_1ELEMENT;
670         case CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_BURST:
671             return CY_DMA_X_LOOP;
672         case CYHAL_DMA_OUTPUT_TRIGGER_ALL_ELEMENTS:
673             return CY_DMA_DESCR;
674         default:
675             // Should never reach here. Just silencing compiler warnings.
676             CY_ASSERT(false);
677             return CY_DMA_DESCR;
678     }
679 }
680 
_cyhal_dma_dw_connect_digital(cyhal_dma_t * obj,cyhal_source_t source,cyhal_dma_input_t input)681 cy_rslt_t _cyhal_dma_dw_connect_digital(cyhal_dma_t *obj, cyhal_source_t source, cyhal_dma_input_t input)
682 {
683     if(input != CYHAL_DMA_INPUT_TRIGGER_SINGLE_ELEMENT &&
684        input != CYHAL_DMA_INPUT_TRIGGER_SINGLE_BURST &&
685        input != CYHAL_DMA_INPUT_TRIGGER_ALL_ELEMENTS)
686         return CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER;
687     // Check that we are not overwriting an existing connection
688     CY_ASSERT(obj->source == CYHAL_TRIGGER_CPUSS_ZERO);
689 
690     obj->descriptor_config.dw.triggerInType = _cyhal_convert_input_t(input);
691     obj->descriptor.dw.ctl &= ~CY_DMA_CTL_TR_IN_TYPE_Msk;
692     obj->descriptor.dw.ctl |= _VAL2FLD(CY_DMA_CTL_TR_IN_TYPE, obj->descriptor_config.dw.triggerInType);
693     #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
694     SCB_CleanDCache_by_Addr((void *)&(obj->descriptor), sizeof(obj->descriptor));
695     #endif /* defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) */
696 
697     cyhal_dest_t dest = _cyhal_dma_dw_get_dest(obj->resource.block_num, obj->resource.channel_num);
698 
699     cy_rslt_t rslt = _cyhal_connect_signal(source, dest);
700     if (CY_RSLT_SUCCESS == rslt)
701     {
702         obj->source = source;
703     }
704 
705     return rslt;
706 }
707 
_cyhal_dma_dw_enable_output(cyhal_dma_t * obj,cyhal_dma_output_t output,cyhal_source_t * source)708 cy_rslt_t _cyhal_dma_dw_enable_output(cyhal_dma_t *obj, cyhal_dma_output_t output, cyhal_source_t *source)
709 {
710     if(output != CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_ELEMENT &&
711        output != CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_BURST &&
712        output != CYHAL_DMA_OUTPUT_TRIGGER_ALL_ELEMENTS)
713         return CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER;
714 
715     obj->descriptor.dw.ctl &= ~CY_DMA_CTL_TR_OUT_TYPE_Msk;
716     obj->descriptor.dw.ctl |= _VAL2FLD(CY_DMA_CTL_TR_OUT_TYPE, _cyhal_convert_output_t(output));
717     #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
718     SCB_CleanDCache_by_Addr((void *)&(obj->descriptor), sizeof(obj->descriptor));
719     #endif /* defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) */
720 
721     *source = _cyhal_dma_dw_get_src(obj->resource.block_num, obj->resource.channel_num);
722 
723     return CY_RSLT_SUCCESS;
724 }
725 
_cyhal_dma_dw_disconnect_digital(cyhal_dma_t * obj,cyhal_source_t source,cyhal_dma_input_t input)726 cy_rslt_t _cyhal_dma_dw_disconnect_digital(cyhal_dma_t *obj, cyhal_source_t source, cyhal_dma_input_t input)
727 {
728     if(input != CYHAL_DMA_INPUT_TRIGGER_SINGLE_ELEMENT &&
729        input != CYHAL_DMA_INPUT_TRIGGER_SINGLE_BURST &&
730        input != CYHAL_DMA_INPUT_TRIGGER_ALL_ELEMENTS)
731         return CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER;
732     CY_ASSERT(obj->source != CYHAL_TRIGGER_CPUSS_ZERO);
733 
734     // There is no option to totally disable. Just reset to default.
735     // NOTE: Use .interruptType since it matches the desired .triggerInType from configure(), but
736     // is not modified by connect/disconnect functions
737     obj->descriptor.dw.ctl &= ~CY_DMA_CTL_TR_IN_TYPE_Msk;
738     obj->descriptor.dw.ctl |= _VAL2FLD(CY_DMA_CTL_TR_IN_TYPE, _cyhal_dma_dw_default_descriptor_config.interruptType);
739     #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
740     SCB_CleanDCache_by_Addr((void *)&(obj->descriptor), sizeof(obj->descriptor));
741     #endif /* defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) */
742 
743     cyhal_dest_t dest = _cyhal_dma_dw_get_dest(obj->resource.block_num, obj->resource.channel_num);
744 
745     cy_rslt_t rslt = _cyhal_disconnect_signal(source, dest);
746     if (CY_RSLT_SUCCESS == rslt)
747     {
748         obj->source = CYHAL_TRIGGER_CPUSS_ZERO;
749     }
750 
751     return rslt;
752 }
753 
_cyhal_dma_dw_disable_output(cyhal_dma_t * obj,cyhal_dma_output_t output)754 cy_rslt_t _cyhal_dma_dw_disable_output(cyhal_dma_t *obj, cyhal_dma_output_t output)
755 {
756     if(output != CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_ELEMENT &&
757        output != CYHAL_DMA_OUTPUT_TRIGGER_SINGLE_BURST &&
758        output != CYHAL_DMA_OUTPUT_TRIGGER_ALL_ELEMENTS)
759         return CYHAL_DMA_RSLT_ERR_INVALID_PARAMETER;
760 
761     // There is no option to totally disable. Just reset to default.
762     obj->descriptor.dw.ctl &= ~CY_DMA_CTL_TR_OUT_TYPE_Msk;
763     obj->descriptor.dw.ctl |= _VAL2FLD(CY_DMA_CTL_TR_OUT_TYPE, _cyhal_dma_dw_default_descriptor_config.triggerOutType);
764     #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
765     SCB_CleanDCache_by_Addr((void *)&(obj->descriptor), sizeof(obj->descriptor));
766     #endif /* defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) */
767 
768     return CY_RSLT_SUCCESS;
769 }
770 
771 #if defined(__cplusplus)
772 }
773 #endif
774 
775 #endif /* _CYHAL_DRIVER_AVAILABLE_DMA_DW */
776