1 /******************************************************************************
2  *
3  * Copyright (C) 2022-2023 Maxim Integrated Products, Inc. (now owned by
4  * Analog Devices, Inc.),
5  * Copyright (C) 2023-2024 Analog Devices, Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  ******************************************************************************/
20 
21 /****** Includes *******/
22 #include <stddef.h>
23 #include <stdint.h>
24 #include "mxc_device.h"
25 #include "mxc_assert.h"
26 #include "mxc_lock.h"
27 #include "mxc_sys.h"
28 #include "dma.h"
29 #include "dma_revb.h"
30 
31 /***** Definitions *****/
32 #define CHECK_HANDLE(x) ((x >= 0) && (x < MXC_DMA_CHANNELS) && (dma_resource[x].valid))
33 
34 typedef struct {
35     void *userCallback; // user given callback
36     void *dest; // memcpy destination
37 } mxc_dma_highlevel_t;
38 
39 typedef struct {
40     unsigned int valid; // Flag to invalidate this resource
41     unsigned int instance; // Hardware instance of this DMA controller
42     unsigned int id; // Channel ID, which matches the index into the underlying hardware
43     mxc_dma_ch_regs_t *regs; // Pointer to the registers for this channel
44     void (*cb)(int, int); // Pointer to a callback function type
45 } mxc_dma_channel_t;
46 
47 /******* Globals *******/
48 static unsigned int dma_initialized = 0;
49 static mxc_dma_channel_t dma_resource[MXC_DMA_CHANNELS];
50 static mxc_dma_highlevel_t memcpy_resource[MXC_DMA_CHANNELS];
51 static uint32_t dma_lock;
52 
53 /****** Functions ******/
54 static void memcpy_callback(int ch, int error);
55 static void transfer_callback(int ch, int error);
56 
MXC_DMA_RevB_Init(void)57 int MXC_DMA_RevB_Init(void)
58 {
59     int i;
60 
61     if (dma_initialized) {
62         return E_BAD_STATE;
63     }
64 
65     //TODO(ADI): Necessary?
66     ///* Initialize any system-level DMA settings */
67     //SYS_DMA_Init();
68 
69     /* Initialize mutex */
70     MXC_FreeLock(&dma_lock);
71 
72     if (MXC_GetLock(&dma_lock, 1) != E_NO_ERROR) {
73         return E_BUSY;
74     }
75 
76     /* Ensure all channels are disabled at start, clear flags, init handles */
77     MXC_DMA->inten = 0;
78 
79     for (i = 0; i < MXC_DMA_CHANNELS; i++) {
80         dma_resource[i].valid = 0;
81         dma_resource[i].instance = 0;
82         dma_resource[i].id = i;
83         dma_resource[i].regs = (mxc_dma_ch_regs_t *)&MXC_DMA->ch[i];
84         dma_resource[i].regs->ctrl = 0;
85         dma_resource[i].regs->status = dma_resource[i].regs->status;
86 
87         dma_resource[i].cb = NULL;
88     }
89 
90     dma_initialized++;
91     MXC_FreeLock(&dma_lock);
92 
93     return E_NO_ERROR;
94 }
95 
MXC_DMA_RevB_AcquireChannel(void)96 int MXC_DMA_RevB_AcquireChannel(void)
97 {
98     int i, channel;
99 
100     /* Check for initialization */
101     if (!dma_initialized) {
102         return E_BAD_STATE;
103     }
104 
105     /* If DMA is locked return busy */
106     if (MXC_GetLock(&dma_lock, 1) != E_NO_ERROR) {
107         return E_BUSY;
108     }
109 
110     /* Default is no channel available */
111     channel = E_NONE_AVAIL;
112 
113     if (dma_initialized) {
114         for (i = 0; i < MXC_DMA_CHANNELS; i++) {
115             if (!dma_resource[i].valid) {
116                 /* Found one */
117                 channel = i;
118                 dma_resource[i].valid = 1;
119                 dma_resource[i].regs->ctrl = 0;
120                 dma_resource[i].regs->cntrld =
121                     0; /* Used by DMA_Start() to conditionally set RLDEN */
122                 break;
123             }
124         }
125     }
126 
127     MXC_FreeLock(&dma_lock);
128 
129     return channel;
130 }
131 
MXC_DMA_RevB_ReleaseChannel(int ch)132 int MXC_DMA_RevB_ReleaseChannel(int ch)
133 {
134     if (CHECK_HANDLE(ch)) {
135         if (MXC_GetLock(&dma_lock, 1) != E_NO_ERROR) {
136             return E_BUSY;
137         }
138 
139         dma_resource[ch].valid = 0;
140         dma_resource[ch].regs->ctrl = 0;
141         dma_resource[ch].regs->status = dma_resource[ch].regs->status;
142         MXC_FreeLock(&dma_lock);
143     } else {
144         return E_BAD_PARAM;
145     }
146 
147     return E_NO_ERROR;
148 }
149 
MXC_DMA_RevB_ConfigChannel(mxc_dma_config_t config,mxc_dma_srcdst_t srcdst)150 int MXC_DMA_RevB_ConfigChannel(mxc_dma_config_t config, mxc_dma_srcdst_t srcdst)
151 {
152     if (CHECK_HANDLE(config.ch)) {
153         /* Designed to be safe, not speedy. Should not be called often */
154         dma_resource[config.ch].regs->ctrl =
155             ((config.srcinc_en ? MXC_F_DMA_CTRL_SRCINC : 0) |
156              (config.dstinc_en ? MXC_F_DMA_CTRL_DSTINC : 0) | config.reqsel |
157              (config.srcwd << MXC_F_DMA_CTRL_SRCWD_POS) |
158              (config.dstwd << MXC_F_DMA_CTRL_DSTWD_POS));
159     } else {
160         return E_BAD_PARAM;
161     }
162 
163     return MXC_DMA_RevB_SetSrcDst(srcdst);
164 }
165 
166 //TODO(ADI): Necessary?
MXC_DMA_RevB_AdvConfigChannel(mxc_dma_adv_config_t advConfig)167 int MXC_DMA_RevB_AdvConfigChannel(mxc_dma_adv_config_t advConfig)
168 {
169     if (CHECK_HANDLE(advConfig.ch) && (advConfig.burst_size > 0)) {
170         dma_resource[advConfig.ch].regs->ctrl &= ~(0x1F00FC0C); // Clear all fields we set here
171         /* Designed to be safe, not speedy. Should not be called often */
172         dma_resource[advConfig.ch].regs->ctrl |=
173             ((advConfig.reqwait_en ? MXC_F_DMA_CTRL_TO_WAIT : 0) | advConfig.prio |
174              advConfig.tosel | advConfig.pssel |
175              (((advConfig.burst_size - 1) << MXC_F_DMA_CTRL_BURST_SIZE_POS) &
176               MXC_F_DMA_CTRL_BURST_SIZE));
177     } else {
178         return E_BAD_PARAM;
179     }
180 
181     return E_NO_ERROR;
182 }
183 
MXC_DMA_RevB_SetSrcDst(mxc_dma_srcdst_t srcdst)184 int MXC_DMA_RevB_SetSrcDst(mxc_dma_srcdst_t srcdst)
185 {
186     if (CHECK_HANDLE(srcdst.ch)) {
187         dma_resource[srcdst.ch].regs->src = (unsigned int)srcdst.source;
188         dma_resource[srcdst.ch].regs->dst = (unsigned int)srcdst.dest;
189         dma_resource[srcdst.ch].regs->cnt = srcdst.len;
190     } else {
191         return E_BAD_PARAM;
192     }
193 
194     return E_NO_ERROR;
195 }
196 
197 //TODO(ADI): Necessary?
MXC_DMA_RevB_GetSrcDst(mxc_dma_srcdst_t * srcdst)198 int MXC_DMA_RevB_GetSrcDst(mxc_dma_srcdst_t *srcdst)
199 {
200     if (CHECK_HANDLE(srcdst.ch)) {
201         srcdst->source = (void *)dma_resource[srcdst->ch].regs->src;
202         srcdst->dest = (void *)dma_resource[srcdst->ch].regs->dst;
203         srcdst->len = (dma_resource[srcdst->ch].regs->cnt) & ~MXC_F_DMA_CNTRLD_EN;
204     } else {
205         return E_BAD_PARAM;
206     }
207 
208     return E_NO_ERROR;
209 }
210 
MXC_DMA_RevB_SetSrcReload(mxc_dma_srcdst_t srcdst)211 int MXC_DMA_RevB_SetSrcReload(mxc_dma_srcdst_t srcdst)
212 {
213     if (CHECK_HANDLE(srcdst.ch)) {
214         dma_resource[srcdst.ch].regs->srcrld = (unsigned int)srcdst.source;
215         dma_resource[srcdst.ch].regs->dstrld = (unsigned int)srcdst.dest;
216 
217         if (dma_resource[srcdst.ch].regs->ctrl & MXC_F_DMA_CTRL_EN) {
218             /* If channel is already running, set RLDEN to enable next reload */
219             dma_resource[srcdst.ch].regs->cntrld = MXC_F_DMA_CNTRLD_EN | srcdst.len;
220         } else {
221             /* Otherwise, this is the initial setup, so DMA_Start() will handle setting that bit */
222             dma_resource[srcdst.ch].regs->cntrld = srcdst.len;
223         }
224     } else {
225         return E_BAD_PARAM;
226     }
227 
228     return E_NO_ERROR;
229 }
230 
231 //TODO(ADI): Necessary?
MXC_DMA_RevB_GetSrcReload(mxc_dma_srcdst_t * srcdst)232 int MXC_DMA_RevB_GetSrcReload(mxc_dma_srcdst_t *srcdst)
233 {
234     if (CHECK_HANDLE(srcdst.ch)) {
235         srcdst->source = (void *)dma_resource[srcdst->ch].regs->srcrld;
236         srcdst->dest = (void *)dma_resource[srcdst->ch].regs->dstrld;
237         srcdst->len = (dma_resource[srcdst->ch].regs->cntrld) & ~MXC_F_DMA_CNTRLD_EN;
238     } else {
239         return E_BAD_PARAM;
240     }
241 
242     return E_NO_ERROR;
243 }
244 
MXC_DMA_RevB_SetCallback(int ch,void (* callback)(int,int))245 int MXC_DMA_RevB_SetCallback(int ch, void (*callback)(int, int))
246 {
247     if (CHECK_HANDLE(ch)) {
248         /* Callback for interrupt handler, no checking is done, as NULL is valid for (none)  */
249         dma_resource[ch].cb = callback;
250     } else {
251         return E_BAD_PARAM;
252     }
253 
254     return E_NO_ERROR;
255 }
256 
257 //TODO(ADI): Necessary?
MXC_DMA_RevB_SetChannelInterruptEn(int ch,int chdis,int ctz)258 int MXC_DMA_RevB_SetChannelInterruptEn(int ch, int chdis, int ctz)
259 {
260     return E_NOT_SUPPORTED;
261 }
262 
263 //TODO(ADI): Necessary?
MXC_DMA_RevB_GetChannelInterruptEn(int ch)264 int MXC_DMA_RevB_GetChannelInterruptEn(int ch)
265 {
266     return E_NOT_SUPPORTED;
267 }
268 
269 //TODO(ADI): Necessary?
MXC_DMA_RevB_ChannelEnableInt(int ch,int flags)270 int MXC_DMA_RevB_ChannelEnableInt(int ch, int flags)
271 {
272     if (CHECK_HANDLE(ch)) {
273         dma_resource[ch].regs->ctrl |= (flags & (MXC_F_DMA_CTRL_DIS_IE | MXC_F_DMA_CTRL_CTZ_IE));
274     } else {
275         return E_BAD_PARAM;
276     }
277 
278     return E_NO_ERROR;
279 }
280 
281 //TODO(ADI): Necessary?
MXC_DMA_RevB_ChannelDisableInt(int ch,int flags)282 int MXC_DMA_RevB_ChannelDisableInt(int ch, int flags)
283 {
284     if (CHECK_HANDLE(ch)) {
285         dma_resource[ch].regs->ctrl &= ~(flags & (MXC_F_DMA_CTRL_DIS_IE | MXC_F_DMA_CTRL_CTZ_IE));
286     } else {
287         return E_BAD_PARAM;
288     }
289 
290     return E_NO_ERROR;
291 }
292 
MXC_DMA_RevB_EnableInt(int ch)293 int MXC_DMA_RevB_EnableInt(int ch)
294 {
295     if (CHECK_HANDLE(ch)) {
296         MXC_DMA->inten |= (1 << ch);
297     } else {
298         return E_BAD_PARAM;
299     }
300 
301     return E_NO_ERROR;
302 }
303 
MXC_DMA_RevB_DisableInt(int ch)304 int MXC_DMA_RevB_DisableInt(int ch)
305 {
306     if (CHECK_HANDLE(ch)) {
307         MXC_DMA->inten &= ~(1 << ch);
308     } else {
309         return E_BAD_PARAM;
310     }
311 
312     return E_NO_ERROR;
313 }
314 
315 //TODO(ADI): Necessary?
MXC_DMA_RevB_ChannelGetFlags(int ch)316 int MXC_DMA_RevB_ChannelGetFlags(int ch)
317 {
318     if (CHECK_HANDLE(ch)) {
319         return dma_resource[ch].regs->status;
320     } else {
321         return E_BAD_PARAM;
322     }
323 
324     return E_NO_ERROR;
325 }
326 
327 //TODO(ADI): Necessary?
MXC_DMA_RevB_ChannelClearFlags(int ch,int flags)328 int MXC_DMA_RevB_ChannelClearFlags(int ch, int flags)
329 {
330     if (CHECK_HANDLE(ch)) {
331         dma_resource[ch].regs->status |= (flags & 0x5F); // Mask for Interrupt flags
332     } else {
333         return E_BAD_PARAM;
334     }
335 
336     return E_NO_ERROR;
337 }
338 
MXC_DMA_RevB_Start(int ch)339 int MXC_DMA_RevB_Start(int ch)
340 {
341     if (CHECK_HANDLE(ch)) {
342         MXC_DMA_ChannelClearFlags(ch, MXC_DMA_RevB_ChannelGetFlags(ch));
343 
344         if (dma_resource[ch].regs->cntrld) {
345             dma_resource[ch].regs->ctrl |= (MXC_F_DMA_CTRL_EN | MXC_F_DMA_CTRL_RLDEN);
346         } else {
347             dma_resource[ch].regs->ctrl |= MXC_F_DMA_CTRL_EN;
348         }
349     } else {
350         return E_BAD_PARAM;
351     }
352 
353     return E_NO_ERROR;
354 }
355 
MXC_DMA_RevB_Stop(int ch)356 int MXC_DMA_RevB_Stop(int ch)
357 {
358     if (CHECK_HANDLE(ch)) {
359         dma_resource[ch].regs->ctrl &= ~MXC_F_DMA_CTRL_EN;
360     } else {
361         return E_BAD_PARAM;
362     }
363 
364     return E_NO_ERROR;
365 }
366 
MXC_DMA_RevB_GetCHRegs(int ch)367 mxc_dma_ch_regs_t *MXC_DMA_RevB_GetCHRegs(int ch)
368 {
369     if (CHECK_HANDLE(ch)) {
370         return dma_resource[ch].regs;
371     } else {
372         return NULL;
373     }
374 }
375 
MXC_DMA_RevB_Handler()376 void MXC_DMA_RevB_Handler()
377 {
378     /* Do callback, if enabled */
379     for (int i = 0; i < MXC_DMA_CHANNELS; i++) {
380         if (CHECK_HANDLE(i)) {
381             if (MXC_DMA->intfl & (0x1 << i)) {
382                 if (dma_resource[i].cb != NULL) {
383                     dma_resource[i].cb(i, E_NO_ERROR);
384                 }
385 
386                 MXC_DMA_ChannelClearFlags(i, MXC_DMA_RevB_ChannelGetFlags(i));
387                 break;
388             }
389         }
390     }
391 }
392 
memcpy_callback(int ch,int error)393 void memcpy_callback(int ch, int error)
394 {
395     mxc_dma_complete_cb_t callback;
396     callback = (mxc_dma_complete_cb_t)memcpy_resource[ch].userCallback;
397 
398     if (error != E_NO_ERROR) {
399         callback(NULL);
400     }
401 
402     callback(memcpy_resource[ch].dest);
403 
404     callback = NULL;
405     MXC_DMA_ReleaseChannel(ch);
406 }
407 
408 //TODO(ADI): Necessary?
MXC_DMA_RevB_MemCpy(void * dest,void * src,int len,mxc_dma_complete_cb_t callback)409 int MXC_DMA_RevB_MemCpy(void *dest, void *src, int len, mxc_dma_complete_cb_t callback)
410 {
411     int retval;
412     mxc_dma_config_t config;
413     mxc_dma_srcdst_t transfer;
414     int channel = MXC_DMA_AcquireChannel();
415 
416     if (memcpy_resource[channel].userCallback != NULL) {
417         // We acquired a channel we haven't cleared yet
418         MXC_DMA_ReleaseChannel(channel);
419         return E_UNKNOWN;
420     }
421 
422     transfer.ch = channel;
423     transfer.source = src;
424     transfer.dest = dest;
425     transfer.len = len;
426 
427     config.ch = channel;
428     config.reqsel = MXC_DMA_REQUEST_MEMTOMEM;
429     config.srcwd = MXC_DMA_WIDTH_WORD;
430     config.dstwd = MXC_DMA_WIDTH_WORD;
431     config.srcinc_en = 1;
432     config.dstinc_en = 1;
433 
434     retval = MXC_DMA_ConfigChannel(config, transfer);
435 
436     if (retval != E_NO_ERROR) {
437         return retval;
438     }
439 
440     retval = MXC_DMA_EnableInt(channel);
441 
442     if (retval != E_NO_ERROR) {
443         return retval;
444     }
445 
446     retval = MXC_DMA_ChannelEnableInt(channel, MXC_F_DMA_CTRL_CTZ_IE);
447 
448     if (retval != E_NO_ERROR) {
449         return retval;
450     }
451 
452     MXC_DMA_SetCallback(channel, memcpy_callback);
453 
454     memcpy_resource[channel].userCallback = (void *)callback;
455     memcpy_resource[channel].dest = dest;
456 
457     return MXC_DMA_Start(channel);
458 }
459 
460 //TODO(ADI): Necessary?
transfer_callback(int ch,int error)461 void transfer_callback(int ch, int error)
462 {
463     // Unimplemented
464     // Check for reason
465     // Call user callback for next transfer
466     // determine whether to load into the transfer slot or reload slot
467     // continue on or stop
468     while (1) {}
469 }
470 
471 //TODO(ADI): Necessary?
MXC_DMA_RevB_DoTransfer(mxc_dma_config_t config,mxc_dma_srcdst_t firstSrcDst,mxc_dma_trans_chain_t callback)472 int MXC_DMA_RevB_DoTransfer(mxc_dma_config_t config, mxc_dma_srcdst_t firstSrcDst,
473                             mxc_dma_trans_chain_t callback)
474 {
475     int retval;
476     int channel = MXC_DMA_AcquireChannel();
477 
478     if (memcpy_resource[channel].userCallback != NULL) {
479         // We acquired a channel we haven't cleared yet
480         MXC_DMA_ReleaseChannel(channel);
481         return E_UNKNOWN;
482     }
483 
484     retval = MXC_DMA_ConfigChannel(config, firstSrcDst);
485 
486     if (retval != E_NO_ERROR) {
487         return retval;
488     }
489 
490     retval = MXC_DMA_EnableInt(channel);
491 
492     if (retval != E_NO_ERROR) {
493         return retval;
494     }
495 
496     retval = MXC_DMA_ChannelEnableInt(channel, MXC_F_DMA_CTRL_CTZ_IE);
497 
498     if (retval != E_NO_ERROR) {
499         return retval;
500     }
501 
502     MXC_DMA_SetCallback(channel, transfer_callback);
503 
504     memcpy_resource[channel].userCallback = (void *)callback;
505 
506     return MXC_DMA_Start(channel);
507 }
508