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_reva.h"
30 #include "dma_reva_regs.h"
31
32 /***** Definitions *****/
33 #define CHECK_HANDLE(x) ((x >= 0) && (x < MXC_DMA_CHANNELS) && (dma_resource[x].valid))
34
35 typedef struct {
36 void *userCallback; // user given callback
37 void *dest; // memcpy destination
38 } mxc_dma_highlevel_t;
39
40 typedef struct {
41 unsigned int valid; // Flag to invalidate this resource
42 unsigned int instance; // Hardware instance of this DMA controller
43 unsigned int id; // Channel ID, which matches the index into the underlying hardware
44 mxc_dma_reva_ch_regs_t *regs; // Pointer to the registers for this channel
45 void (*cb)(int, int); // Pointer to a callback function type
46 } mxc_dma_channel_t;
47
48 /******* Globals *******/
49 static unsigned int dma_initialized[MXC_DMA_INSTANCES] = { 0 };
50 static mxc_dma_channel_t dma_resource[MXC_DMA_CHANNELS];
51 static mxc_dma_highlevel_t memcpy_resource[MXC_DMA_CHANNELS];
52 static uint32_t dma_lock;
53
54 /****** Functions ******/
55 static void memcpy_callback(int ch, int error);
56 static void transfer_callback(int ch, int error);
57
MXC_DMA_RevA_Init(mxc_dma_reva_regs_t * dma)58 int MXC_DMA_RevA_Init(mxc_dma_reva_regs_t *dma)
59 {
60 int i, numCh, offset;
61 int dma_idx;
62
63 dma_idx = MXC_DMA_GET_IDX((mxc_dma_regs_t *)dma);
64 MXC_ASSERT(dma_idx >= 0);
65
66 #if TARGET_NUM == 32665
67 numCh = MXC_DMA_CH_OFFSET;
68 offset = numCh * dma_idx;
69 #else
70 numCh = MXC_DMA_CHANNELS;
71 offset = 0;
72 #endif
73
74 if (dma_initialized[dma_idx]) {
75 return E_BAD_STATE;
76 }
77
78 #ifndef __riscv
79 /* Initialize mutex */
80 MXC_FreeLock(&dma_lock);
81
82 if (MXC_GetLock(&dma_lock, 1) != E_NO_ERROR) {
83 return E_BUSY;
84 }
85 #endif
86
87 /* Ensure all channels are disabled at start, clear flags, init handles */
88 dma->inten = 0;
89
90 for (i = offset; i < (offset + numCh); i++) {
91 dma_resource[i].valid = 0;
92 dma_resource[i].instance = 0;
93 dma_resource[i].id = i;
94 dma_resource[i].regs = (mxc_dma_reva_ch_regs_t *)&(dma->ch[(i % numCh)]);
95 dma_resource[i].regs->ctrl = 0;
96 dma_resource[i].regs->status = dma_resource[i].regs->status;
97
98 dma_resource[i].cb = NULL;
99 }
100
101 dma_initialized[dma_idx]++;
102 #ifndef __riscv
103 MXC_FreeLock(&dma_lock);
104 #endif
105
106 return E_NO_ERROR;
107 }
108
MXC_DMA_RevA_DeInit(mxc_dma_reva_regs_t * dma)109 void MXC_DMA_RevA_DeInit(mxc_dma_reva_regs_t *dma)
110 {
111 int dma_idx;
112
113 dma_idx = MXC_DMA_GET_IDX((mxc_dma_regs_t *)dma);
114 MXC_ASSERT(dma_idx >= 0);
115
116 dma_initialized[dma_idx] = 0;
117 }
118
MXC_DMA_RevA_AcquireChannel(mxc_dma_reva_regs_t * dma)119 int MXC_DMA_RevA_AcquireChannel(mxc_dma_reva_regs_t *dma)
120 {
121 int i, channel, numCh, offset;
122 int dma_idx;
123
124 dma_idx = MXC_DMA_GET_IDX((mxc_dma_regs_t *)dma);
125 MXC_ASSERT(dma_idx >= 0);
126
127 /* Check for initialization */
128 if (!dma_initialized[dma_idx]) {
129 return E_BAD_STATE;
130 }
131
132 #if TARGET_NUM == 32665
133 numCh = MXC_DMA_CH_OFFSET;
134 offset = MXC_DMA_CH_OFFSET * dma_idx;
135 #else
136 numCh = MXC_DMA_CHANNELS;
137 offset = 0;
138 #endif
139
140 #ifndef __riscv
141 /* If DMA is locked return busy */
142 if (MXC_GetLock(&dma_lock, 1) != E_NO_ERROR) {
143 return E_BUSY;
144 }
145 #endif
146 /* Default is no channel available */
147 channel = E_NONE_AVAIL;
148
149 for (i = offset; i < (offset + numCh); i++) {
150 if (!dma_resource[i].valid) {
151 /* Found one */
152 channel = i;
153 dma_resource[i].valid = 1;
154 dma_resource[i].regs->ctrl = 0;
155 dma_resource[i].regs->cntrld = 0; /* Used by DMA_Start() to conditionally set RLDEN */
156 break;
157 }
158 }
159 #ifndef __riscv
160 MXC_FreeLock(&dma_lock);
161 #endif
162
163 return channel;
164 }
165
MXC_DMA_RevA_ReleaseChannel(int ch)166 int MXC_DMA_RevA_ReleaseChannel(int ch)
167 {
168 if (CHECK_HANDLE(ch)) {
169 if (MXC_GetLock(&dma_lock, 1) != E_NO_ERROR) {
170 return E_BUSY;
171 }
172
173 dma_resource[ch].valid = 0;
174 dma_resource[ch].regs->ctrl = 0;
175 dma_resource[ch].regs->status = dma_resource[ch].regs->status;
176 MXC_FreeLock(&dma_lock);
177 } else {
178 return E_BAD_PARAM;
179 }
180
181 return E_NO_ERROR;
182 }
183
MXC_DMA_RevA_ConfigChannel(mxc_dma_config_t config,mxc_dma_srcdst_t srcdst)184 int MXC_DMA_RevA_ConfigChannel(mxc_dma_config_t config, mxc_dma_srcdst_t srcdst)
185 {
186 if (CHECK_HANDLE(config.ch)) {
187 /* Designed to be safe, not speedy. Should not be called often */
188 dma_resource[config.ch].regs->ctrl =
189 ((config.srcinc_en ? MXC_F_DMA_REVA_CTRL_SRCINC : 0) |
190 (config.dstinc_en ? MXC_F_DMA_REVA_CTRL_DSTINC : 0) | config.reqsel |
191 (config.srcwd << MXC_F_DMA_REVA_CTRL_SRCWD_POS) |
192 (config.dstwd << MXC_F_DMA_REVA_CTRL_DSTWD_POS));
193 } else {
194 return E_BAD_PARAM;
195 }
196
197 return MXC_DMA_RevA_SetSrcDst(srcdst);
198 }
199
MXC_DMA_RevA_AdvConfigChannel(mxc_dma_adv_config_t advConfig)200 int MXC_DMA_RevA_AdvConfigChannel(mxc_dma_adv_config_t advConfig)
201 {
202 if (CHECK_HANDLE(advConfig.ch) && (advConfig.burst_size > 0)) {
203 dma_resource[advConfig.ch].regs->ctrl &= ~(0x1F00FC0C); // Clear all fields we set here
204 /* Designed to be safe, not speedy. Should not be called often */
205 dma_resource[advConfig.ch].regs->ctrl |=
206 ((advConfig.reqwait_en ? MXC_F_DMA_REVA_CTRL_TO_WAIT : 0) | advConfig.prio |
207 advConfig.tosel | advConfig.pssel |
208 (((advConfig.burst_size - 1) << MXC_F_DMA_REVA_CTRL_BURST_SIZE_POS) &
209 MXC_F_DMA_REVA_CTRL_BURST_SIZE));
210 } else {
211 return E_BAD_PARAM;
212 }
213
214 return E_NO_ERROR;
215 }
216
MXC_DMA_RevA_SetSrcDst(mxc_dma_srcdst_t srcdst)217 int MXC_DMA_RevA_SetSrcDst(mxc_dma_srcdst_t srcdst)
218 {
219 if (CHECK_HANDLE(srcdst.ch)) {
220 dma_resource[srcdst.ch].regs->src = (unsigned int)srcdst.source;
221 dma_resource[srcdst.ch].regs->dst = (unsigned int)srcdst.dest;
222 dma_resource[srcdst.ch].regs->cnt = srcdst.len;
223 } else {
224 return E_BAD_PARAM;
225 }
226
227 return E_NO_ERROR;
228 }
229
MXC_DMA_RevA_GetSrcDst(mxc_dma_srcdst_t * srcdst)230 int MXC_DMA_RevA_GetSrcDst(mxc_dma_srcdst_t *srcdst)
231 {
232 if (CHECK_HANDLE(srcdst->ch)) {
233 srcdst->source = (void *)dma_resource[srcdst->ch].regs->src;
234 srcdst->dest = (void *)dma_resource[srcdst->ch].regs->dst;
235 srcdst->len = (dma_resource[srcdst->ch].regs->cnt) & ~MXC_F_DMA_REVA_CNTRLD_EN;
236 } else {
237 return E_BAD_PARAM;
238 }
239
240 return E_NO_ERROR;
241 }
242
MXC_DMA_RevA_SetSrcReload(mxc_dma_srcdst_t srcdst)243 int MXC_DMA_RevA_SetSrcReload(mxc_dma_srcdst_t srcdst)
244 {
245 if (CHECK_HANDLE(srcdst.ch)) {
246 dma_resource[srcdst.ch].regs->srcrld = (unsigned int)srcdst.source;
247 dma_resource[srcdst.ch].regs->dstrld = (unsigned int)srcdst.dest;
248
249 if (dma_resource[srcdst.ch].regs->ctrl & MXC_F_DMA_REVA_CTRL_EN) {
250 /* If channel is already running, set RLDEN to enable next reload */
251 dma_resource[srcdst.ch].regs->cntrld = MXC_F_DMA_REVA_CNTRLD_EN | srcdst.len;
252 } else {
253 /* Otherwise, this is the initial setup, so DMA_Start() will handle setting that bit */
254 dma_resource[srcdst.ch].regs->cntrld = srcdst.len;
255 }
256 } else {
257 return E_BAD_PARAM;
258 }
259
260 return E_NO_ERROR;
261 }
262
MXC_DMA_RevA_GetSrcReload(mxc_dma_srcdst_t * srcdst)263 int MXC_DMA_RevA_GetSrcReload(mxc_dma_srcdst_t *srcdst)
264 {
265 if (CHECK_HANDLE(srcdst->ch)) {
266 srcdst->source = (void *)dma_resource[srcdst->ch].regs->srcrld;
267 srcdst->dest = (void *)dma_resource[srcdst->ch].regs->dstrld;
268 srcdst->len = (dma_resource[srcdst->ch].regs->cntrld) & ~MXC_F_DMA_REVA_CNTRLD_EN;
269 } else {
270 return E_BAD_PARAM;
271 }
272
273 return E_NO_ERROR;
274 }
275
MXC_DMA_RevA_SetCallback(int ch,void (* callback)(int,int))276 int MXC_DMA_RevA_SetCallback(int ch, void (*callback)(int, int))
277 {
278 if (CHECK_HANDLE(ch)) {
279 /* Callback for interrupt handler, no checking is done, as NULL is valid for(none) */
280 dma_resource[ch].cb = callback;
281 } else {
282 return E_BAD_PARAM;
283 }
284
285 return E_NO_ERROR;
286 }
287
MXC_DMA_RevA_SetChannelInterruptEn(int ch,bool chdis,bool ctz)288 int MXC_DMA_RevA_SetChannelInterruptEn(int ch, bool chdis, bool ctz)
289 {
290 if (CHECK_HANDLE(ch)) {
291 if (chdis) {
292 dma_resource[ch].regs->ctrl |= (MXC_F_DMA_REVA_CTRL_DIS_IE);
293 }
294 if (ctz) {
295 dma_resource[ch].regs->ctrl |= (MXC_F_DMA_REVA_CTRL_CTZ_IE);
296 }
297 } else {
298 return E_BAD_PARAM;
299 }
300
301 return E_NO_ERROR;
302 }
303
MXC_DMA_RevA_GetChannelInterruptEn(int ch)304 int MXC_DMA_RevA_GetChannelInterruptEn(int ch)
305 {
306 return E_NOT_SUPPORTED;
307 }
308
MXC_DMA_RevA_ChannelEnableInt(int ch,int flags)309 int MXC_DMA_RevA_ChannelEnableInt(int ch, int flags)
310 {
311 if (CHECK_HANDLE(ch)) {
312 dma_resource[ch].regs->ctrl |=
313 (flags & (MXC_F_DMA_REVA_CTRL_DIS_IE | MXC_F_DMA_REVA_CTRL_CTZ_IE));
314 } else {
315 return E_BAD_PARAM;
316 }
317
318 return E_NO_ERROR;
319 }
320
MXC_DMA_RevA_ChannelDisableInt(int ch,int flags)321 int MXC_DMA_RevA_ChannelDisableInt(int ch, int flags)
322 {
323 if (CHECK_HANDLE(ch)) {
324 dma_resource[ch].regs->ctrl &=
325 ~(flags & (MXC_F_DMA_REVA_CTRL_DIS_IE | MXC_F_DMA_REVA_CTRL_CTZ_IE));
326 } else {
327 return E_BAD_PARAM;
328 }
329
330 return E_NO_ERROR;
331 }
332
MXC_DMA_RevA_EnableInt(mxc_dma_reva_regs_t * dma,int ch)333 int MXC_DMA_RevA_EnableInt(mxc_dma_reva_regs_t *dma, int ch)
334 {
335 if (CHECK_HANDLE(ch)) {
336 #if TARGET_NUM == 32665
337 ch %= MXC_DMA_CH_OFFSET;
338 #endif
339 dma->inten |= (1 << ch);
340 } else {
341 return E_BAD_PARAM;
342 }
343
344 return E_NO_ERROR;
345 }
346
MXC_DMA_RevA_DisableInt(mxc_dma_reva_regs_t * dma,int ch)347 int MXC_DMA_RevA_DisableInt(mxc_dma_reva_regs_t *dma, int ch)
348 {
349 if (CHECK_HANDLE(ch)) {
350 #if TARGET_NUM == 32665
351 ch %= MXC_DMA_CH_OFFSET;
352 #endif
353 dma->inten &= ~(1 << ch);
354 } else {
355 return E_BAD_PARAM;
356 }
357
358 return E_NO_ERROR;
359 }
360
MXC_DMA_RevA_ChannelGetFlags(int ch)361 int MXC_DMA_RevA_ChannelGetFlags(int ch)
362 {
363 if (CHECK_HANDLE(ch)) {
364 return dma_resource[ch].regs->status;
365 } else {
366 return E_BAD_PARAM;
367 }
368 }
369
MXC_DMA_RevA_ChannelClearFlags(int ch,int flags)370 int MXC_DMA_RevA_ChannelClearFlags(int ch, int flags)
371 {
372 if (CHECK_HANDLE(ch)) {
373 dma_resource[ch].regs->status |= (flags & 0x5F); // Mask for Interrupt flags
374 } else {
375 return E_BAD_PARAM;
376 }
377
378 return E_NO_ERROR;
379 }
380
MXC_DMA_RevA_Start(int ch)381 int MXC_DMA_RevA_Start(int ch)
382 {
383 if (CHECK_HANDLE(ch)) {
384 MXC_DMA_ChannelClearFlags(ch, MXC_DMA_RevA_ChannelGetFlags(ch));
385
386 if (dma_resource[ch].regs->cntrld) {
387 dma_resource[ch].regs->ctrl |= (MXC_F_DMA_REVA_CTRL_EN | MXC_F_DMA_REVA_CTRL_RLDEN);
388 } else {
389 dma_resource[ch].regs->ctrl |= MXC_F_DMA_REVA_CTRL_EN;
390 }
391 } else {
392 return E_BAD_PARAM;
393 }
394
395 return E_NO_ERROR;
396 }
397
MXC_DMA_RevA_Stop(int ch)398 int MXC_DMA_RevA_Stop(int ch)
399 {
400 if (CHECK_HANDLE(ch)) {
401 dma_resource[ch].regs->ctrl &= ~MXC_F_DMA_REVA_CTRL_EN;
402 } else {
403 return E_BAD_PARAM;
404 }
405
406 return E_NO_ERROR;
407 }
408
MXC_DMA_RevA_GetCHRegs(int ch)409 mxc_dma_ch_regs_t *MXC_DMA_RevA_GetCHRegs(int ch)
410 {
411 if (CHECK_HANDLE(ch)) {
412 return (mxc_dma_ch_regs_t *)dma_resource[ch].regs;
413 } else {
414 return NULL;
415 }
416 }
417
MXC_DMA_RevA_Handler(mxc_dma_reva_regs_t * dma)418 void MXC_DMA_RevA_Handler(mxc_dma_reva_regs_t *dma)
419 {
420 int numCh = MXC_DMA_CHANNELS / MXC_DMA_INSTANCES;
421 int dma_idx;
422 int offset;
423
424 dma_idx = MXC_DMA_GET_IDX((mxc_dma_regs_t *)dma);
425 MXC_ASSERT(dma_idx >= 0);
426
427 offset = numCh * dma_idx;
428 /* Do callback, if enabled */
429 for (int i = offset; i < (offset + numCh); i++) {
430 if (CHECK_HANDLE(i)) {
431 if (dma->intfl & (0x1 << (i % numCh))) {
432 if (dma_resource[i].cb != NULL) {
433 dma_resource[i].cb(i, E_NO_ERROR);
434 }
435
436 MXC_DMA_ChannelClearFlags(i, MXC_DMA_RevA_ChannelGetFlags(i));
437
438 // No need to check rest of the channels if no interrupt flags set.
439 if (dma->intfl == 0) {
440 break;
441 }
442 }
443 }
444 }
445 }
446
memcpy_callback(int ch,int error)447 void memcpy_callback(int ch, int error)
448 {
449 mxc_dma_complete_cb_t callback;
450 callback = (mxc_dma_complete_cb_t)memcpy_resource[ch].userCallback;
451
452 if (error != E_NO_ERROR) {
453 callback(NULL);
454 }
455
456 callback(memcpy_resource[ch].dest);
457
458 // Release global objects and local resources
459 callback = NULL;
460 memcpy_resource[ch].userCallback = NULL;
461 memcpy_resource[ch].dest = NULL;
462 MXC_DMA_ReleaseChannel(ch);
463 }
464
MXC_DMA_RevA_MemCpy(mxc_dma_reva_regs_t * dma,void * dest,void * src,int len,mxc_dma_complete_cb_t callback)465 int MXC_DMA_RevA_MemCpy(mxc_dma_reva_regs_t *dma, void *dest, void *src, int len,
466 mxc_dma_complete_cb_t callback)
467 {
468 int retval;
469 mxc_dma_config_t config;
470 mxc_dma_srcdst_t transfer;
471 int channel;
472
473 #if TARGET_NUM == 32665
474 channel = MXC_DMA_AcquireChannel((mxc_dma_regs_t *)dma);
475 #else
476 channel = MXC_DMA_AcquireChannel();
477 #endif
478
479 if (memcpy_resource[channel].userCallback != NULL) {
480 // We acquired a channel we haven't cleared yet
481 MXC_DMA_ReleaseChannel(channel);
482 return E_UNKNOWN;
483 }
484
485 transfer.ch = channel;
486 transfer.source = src;
487 transfer.dest = dest;
488 transfer.len = len;
489
490 config.ch = channel;
491 config.reqsel = MXC_DMA_REQUEST_MEMTOMEM;
492 config.srcwd = MXC_DMA_WIDTH_WORD;
493 config.dstwd = MXC_DMA_WIDTH_WORD;
494 config.srcinc_en = 1;
495 config.dstinc_en = 1;
496
497 retval = MXC_DMA_ConfigChannel(config, transfer);
498
499 if (retval != E_NO_ERROR) {
500 return retval;
501 }
502
503 retval = MXC_DMA_EnableInt(channel);
504
505 if (retval != E_NO_ERROR) {
506 return retval;
507 }
508
509 retval = MXC_DMA_ChannelEnableInt(channel, MXC_F_DMA_REVA_CTRL_CTZ_IE);
510
511 if (retval != E_NO_ERROR) {
512 return retval;
513 }
514
515 MXC_DMA_SetCallback(channel, memcpy_callback);
516
517 memcpy_resource[channel].userCallback = (void *)callback;
518 memcpy_resource[channel].dest = dest;
519
520 return MXC_DMA_Start(channel);
521 }
522
transfer_callback(int ch,int error)523 void transfer_callback(int ch, int error)
524 {
525 // Unimplemented
526 // Check for reason
527 // Call user callback for next transfer
528 // determine whether to load into the transfer slot or reload slot
529 // continue on or stop
530 while (1) {}
531 }
532
MXC_DMA_RevA_DoTransfer(mxc_dma_reva_regs_t * dma,mxc_dma_config_t config,mxc_dma_srcdst_t firstSrcDst,mxc_dma_trans_chain_t callback)533 int MXC_DMA_RevA_DoTransfer(mxc_dma_reva_regs_t *dma, mxc_dma_config_t config,
534 mxc_dma_srcdst_t firstSrcDst, mxc_dma_trans_chain_t callback)
535 {
536 int retval, channel;
537
538 #if TARGET_NUM == 32665
539 channel = MXC_DMA_AcquireChannel((mxc_dma_regs_t *)dma);
540 #else
541 channel = MXC_DMA_AcquireChannel();
542 #endif
543
544 if (memcpy_resource[channel].userCallback != NULL) {
545 // We acquired a channel we haven't cleared yet
546 MXC_DMA_ReleaseChannel(channel);
547 return E_UNKNOWN;
548 }
549
550 retval = MXC_DMA_ConfigChannel(config, firstSrcDst);
551
552 if (retval != E_NO_ERROR) {
553 return retval;
554 }
555
556 retval = MXC_DMA_EnableInt(channel);
557
558 if (retval != E_NO_ERROR) {
559 return retval;
560 }
561
562 retval = MXC_DMA_ChannelEnableInt(channel, MXC_F_DMA_REVA_CTRL_CTZ_IE);
563
564 if (retval != E_NO_ERROR) {
565 return retval;
566 }
567
568 MXC_DMA_SetCallback(channel, transfer_callback);
569
570 memcpy_resource[channel].userCallback = (void *)callback;
571
572 return MXC_DMA_Start(channel);
573 }
574