summaryrefslogtreecommitdiff
path: root/bsp/maxim/libraries/MAX32660PeriphDriver/Source/dma.c
blob: 51aac68d34d0c4b385823a8ade14521ad2b81072 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
/* *****************************************************************************
 * Copyright (C) 2017 Maxim Integrated Products, Inc., All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Maxim Integrated
 * Products, Inc. shall not be used except as stated in the Maxim Integrated
 * Products, Inc. Branding Policy.
 *
 * The mere transfer of this software does not imply any licenses
 * of trade secrets, proprietary technology, copyrights, patents,
 * trademarks, maskwork rights, or any other form of intellectual
 * property whatsoever. Maxim Integrated Products, Inc. retains all
 * ownership rights.
 *
 * $Date: 2019-07-01 11:06:19 -0500 (Mon, 01 Jul 2019) $
 * $Revision: 44383 $
 *
 **************************************************************************** */

#include <stddef.h>
#include <stdint.h>
#include "mxc_config.h"
#include "mxc_assert.h"
#include "mxc_lock.h"
#include "mxc_sys.h"
#include "dma.h"

/*
 * Structure type
 */
typedef struct {
    unsigned int valid;         /* Flag to invalidate this resource */
    unsigned int instance;      /* Hardware instance of this DMA controller */
    unsigned int id;            /* Channel ID, which matches the index into the underlying hardware */
    mxc_dma_ch_regs_t *regs;    /* Pointer to the registers for this channel */
    void (*cb)(int, int);       /* Pointer to a callback function type */
} dma_channel_t;

#define CHECK_HANDLE(x) ((x >= 0) && (x < MXC_DMA_CHANNELS) && (dma_resource[x].valid))

/* DMA driver must be initialized once before use, and may not be initialized again without shutdown, as it is a shared resource */
static unsigned int dma_initialized = 0;

static dma_channel_t dma_resource[MXC_DMA_CHANNELS];

static uint32_t dma_lock;

/* Initialize DMA to known state */
int DMA_Init(void)
{
    int i;
    
    if (dma_initialized) {
        return E_BAD_STATE;
    }
    
    /* Initialize any system-level DMA settings */
    SYS_DMA_Init();
    
    /* Initialize mutex */
    mxc_free_lock(&dma_lock);
    if (mxc_get_lock(&dma_lock, 1) != E_NO_ERROR) {
        return E_BUSY;
    }
    
    /* Ensure all channels are disabled at start, clear flags, init handles */
    MXC_DMA->cn = 0;
    for (i = 0; i < MXC_DMA_CHANNELS; i++) {
        dma_resource[i].valid = 0;
        dma_resource[i].instance = 0;
        dma_resource[i].id = i;
        dma_resource[i].regs = (mxc_dma_ch_regs_t *)&MXC_DMA->ch[i];
        dma_resource[i].regs->cfg = 0;
        dma_resource[i].regs->st = dma_resource[i].regs->st;
        
        dma_resource[i].cb = NULL;
    }
    dma_initialized++;
    mxc_free_lock(&dma_lock);
    
    return E_NO_ERROR;
}

/* Shut down DMA in an orderly manner, informing clients that their requests did not complete */
int DMA_Shutdown(void)
{
    int i;
    
    if (!dma_initialized) {
        /* Never initialized, so shutdown is not appropriate */
        return E_BUSY;
    }
    
    if (mxc_get_lock(&dma_lock, 1) != E_NO_ERROR) {
        return E_BUSY;
    }
    
    /* Prevent any new resource allocation by this API */
    dma_initialized = 0;
    /* Disable interrupts, preventing future callbacks */
    MXC_DMA->cn = 0;
    
    /* For each channel:
     *  - invalidate the handles held by clients
     *  - stop any transfer in progress
     */
    for (i = 0; i < MXC_DMA_CHANNELS; i++) {
        dma_resource[i].regs->cfg = 0;
        if (dma_resource[i].valid) {
            dma_resource[i].valid = 0;
            if (dma_resource[i].cb != NULL) {
                dma_resource[i].cb(i, E_SHUTDOWN);
            }
        }
    }
    
    /* Disable any system-level DMA settings */
    SYS_DMA_Shutdown();
    
    mxc_free_lock(&dma_lock);
    
    return E_NO_ERROR;
}

/* Request DMA channel */
/* Once "owned", this channel may be used directly via the DMA_GetCHRegs(ch) pointer, or */
/* configured via the API functions */
int DMA_AcquireChannel(void)
{
    int i, channel;
    
    /* Check for initialization */
    if (!dma_initialized) {
        return E_BAD_STATE;
    }
    
    /* If DMA is locked return busy */
    if (mxc_get_lock(&dma_lock, 1) != E_NO_ERROR) {
        return E_BUSY;
    }
    
    /* Default is no channel available */
    channel = E_NONE_AVAIL;
    if (dma_initialized) {
        for (i = 0; i < MXC_DMA_CHANNELS; i++) {
            if (!dma_resource[i].valid) {
                /* Found one */
                channel = i;
                dma_resource[i].valid = 1;
                dma_resource[i].regs->cfg = 0;
                dma_resource[i].regs->cnt_rld = 0; /* Used by DMA_Start() to conditionally set RLDEN */
                break;
            }
        }
    }
    mxc_free_lock(&dma_lock);
    
    return channel;
}

/* Release DMA channel */
/* Callbacks will not be called */
int DMA_ReleaseChannel(int ch)
{
    if (CHECK_HANDLE(ch)) {
        if (mxc_get_lock(&dma_lock, 1) != E_NO_ERROR) {
            return E_BUSY;
        }
        dma_resource[ch].valid = 0;
        dma_resource[ch].regs->cfg = 0;
        dma_resource[ch].regs->st = dma_resource[ch].regs->st;
        mxc_free_lock(&dma_lock);
    } else {
        return E_BAD_PARAM;
    }
    
    return E_NO_ERROR;
}

/* Channel configuration */
int DMA_ConfigChannel(int ch,
                      dma_priority_t prio,
                      dma_reqsel_t reqsel, unsigned int reqwait_en,
                      dma_timeout_t tosel, dma_prescale_t pssel,
                      dma_width_t srcwd, unsigned int srcinc_en,
                      dma_width_t dstwd, unsigned int dstinc_en,
                      unsigned int burst_size, unsigned int chdis_inten,
                      unsigned int ctz_inten)
{
    if (CHECK_HANDLE(ch) && (burst_size > 0)) {
        /* Designed to be safe, not speedy. Should not be called often */
        dma_resource[ch].regs->cfg =
            ((reqwait_en ? MXC_F_DMA_CFG_REQWAIT : 0) |
             (srcinc_en ? MXC_F_DMA_CFG_SRCINC : 0)   |
             (dstinc_en ? MXC_F_DMA_CFG_DSTINC : 0)   |
             (chdis_inten ? MXC_F_DMA_CFG_CHDIEN : 0) |
             (ctz_inten ? MXC_F_DMA_CFG_CTZIEN : 0)   |
             prio |reqsel | tosel | pssel | 
             (srcwd << MXC_F_DMA_CFG_SRCWD_POS) |
             (dstwd << MXC_F_DMA_CFG_DSTWD_POS) |
             (((burst_size - 1) << MXC_F_DMA_CFG_BRST_POS) & MXC_F_DMA_CFG_BRST));
    } else {
        return E_BAD_PARAM;
    }
    
    return E_NO_ERROR;
}

/*
 * DMA request selects for peripherals will override either src_addr or dst_addr.
 * In these cases, the overridden address is a don't care and may be 0.
 */
int DMA_SetSrcDstCnt(int ch,
                     void *src_addr,
                     void *dst_addr,
                     unsigned int count)
{
    if (CHECK_HANDLE(ch)) {
        dma_resource[ch].regs->src = (unsigned int)src_addr;
        dma_resource[ch].regs->dst = (unsigned int)dst_addr;
        dma_resource[ch].regs->cnt = count;
    } else {
        return E_BAD_PARAM;
    }
    
    return E_NO_ERROR;
}

/* Must set en_reload == 1 to have any effect */
int DMA_SetReload(int ch,
                  void *src_addr_reload,
                  void *dst_addr_reload,
                  unsigned int count_reload)
{
    if (CHECK_HANDLE(ch)) {
        dma_resource[ch].regs->src_rld = (unsigned int)src_addr_reload;
        dma_resource[ch].regs->dst_rld = (unsigned int)dst_addr_reload;
        if (dma_resource[ch].regs->cfg & MXC_F_DMA_CFG_CHEN) {
            /* If channel is already running, set RLDEN to enable next reload */
            dma_resource[ch].regs->cnt_rld = MXC_F_DMA_CNT_RLD_RLDEN | count_reload;
        } else {
            /* Otherwise, this is the initial setup, so DMA_Start() will handle setting that bit */
            dma_resource[ch].regs->cnt_rld = count_reload;
        }
    } else {
        return E_BAD_PARAM;
    }
    
    return E_NO_ERROR;
}

int DMA_SetCallback(int ch, void (*callback)(int, int))
{
    if (CHECK_HANDLE(ch)) {
        /* Callback for interrupt handler, no checking is done, as NULL is valid for (none)  */
        dma_resource[ch].cb = callback;
    } else {
        return E_BAD_PARAM;
    }
    
    return E_NO_ERROR;
}

/* Interrupt enable/disable */
int DMA_EnableInterrupt(int ch)
{
    if (CHECK_HANDLE(ch)) {
        MXC_DMA->cn |= (1 << ch);
    } else {
        return E_BAD_PARAM;
    }
    
    return E_NO_ERROR;
}

int DMA_DisableInterrupt(int ch)
{
    if (CHECK_HANDLE(ch)) {
        MXC_DMA->cn &= ~(1 << ch);
    } else {
        return E_BAD_PARAM;
    }
    
    return E_NO_ERROR;
}

/* Channel interrupt flags */
int DMA_GetFlags(int ch, unsigned int *fl)
{
    if (CHECK_HANDLE(ch) && fl) {
        *fl = dma_resource[ch].regs->st;
    } else {
        return E_BAD_PARAM;
    }
    
    return E_NO_ERROR;
}

int DMA_ClearFlags(int ch)
{
    if (CHECK_HANDLE(ch)) {
        dma_resource[ch].regs->st = dma_resource[ch].regs->st;
    } else {
        return E_BAD_PARAM;
    }
    
    return E_NO_ERROR;
}

/* Start channel */
int DMA_Start(int ch)
{
    if (CHECK_HANDLE(ch)) {
        DMA_ClearFlags(ch);
        if (dma_resource[ch].regs->cnt_rld) {
            dma_resource[ch].regs->cfg |= (MXC_F_DMA_CFG_CHEN | MXC_F_DMA_CFG_RLDEN);
        } else {
            dma_resource[ch].regs->cfg |= MXC_F_DMA_CFG_CHEN;
        }
    } else {
        return E_BAD_PARAM;
    }
    
    return E_NO_ERROR;
}

/* Stop channel */
int DMA_Stop(int ch)
{
    if (CHECK_HANDLE(ch)) {
        dma_resource[ch].regs->cfg &= ~MXC_F_DMA_CFG_CHEN;
    } else {
        return E_BAD_PARAM;
    }
    
    return E_NO_ERROR;
}

/* Get pointer to registers, for advanced users */
mxc_dma_ch_regs_t *DMA_GetCHRegs(int ch)
{
    if (CHECK_HANDLE(ch)) {
        return dma_resource[ch].regs;
    } else {
        return NULL;
    }
}

/* */
void DMA_Handler(int ch)
{
    /* Do callback, if enabled */
    if (dma_resource[ch].cb != NULL) {
        dma_resource[ch].cb(ch, E_NO_ERROR);
    }
    DMA_ClearFlags(ch);
}