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