1 /* *****************************************************************************
2 * Copyright (C) 2017 Maxim Integrated Products, Inc., All Rights Reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
18 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Except as contained in this notice, the name of Maxim Integrated
23 * Products, Inc. shall not be used except as stated in the Maxim Integrated
24 * Products, Inc. Branding Policy.
25 *
26 * The mere transfer of this software does not imply any licenses
27 * of trade secrets, proprietary technology, copyrights, patents,
28 * trademarks, maskwork rights, or any other form of intellectual
29 * property whatsoever. Maxim Integrated Products, Inc. retains all
30 * ownership rights.
31 *
32 * $Date: 2019-07-01 11:06:19 -0500 (Mon, 01 Jul 2019) $
33 * $Revision: 44383 $
34 *
35 **************************************************************************** */
36
37 #include <stddef.h>
38 #include <stdint.h>
39 #include "mxc_config.h"
40 #include "mxc_assert.h"
41 #include "mxc_lock.h"
42 #include "mxc_sys.h"
43 #include "dma.h"
44
45 /*
46 * Structure type
47 */
48 typedef struct {
49 unsigned int valid; /* Flag to invalidate this resource */
50 unsigned int instance; /* Hardware instance of this DMA controller */
51 unsigned int id; /* Channel ID, which matches the index into the underlying hardware */
52 mxc_dma_ch_regs_t *regs; /* Pointer to the registers for this channel */
53 void (*cb)(int, int); /* Pointer to a callback function type */
54 } dma_channel_t;
55
56 #define CHECK_HANDLE(x) ((x >= 0) && (x < MXC_DMA_CHANNELS) && (dma_resource[x].valid))
57
58 /* DMA driver must be initialized once before use, and may not be initialized again without shutdown, as it is a shared resource */
59 static unsigned int dma_initialized = 0;
60
61 static dma_channel_t dma_resource[MXC_DMA_CHANNELS];
62
63 static uint32_t dma_lock;
64
65 /* Initialize DMA to known state */
DMA_Init(void)66 int DMA_Init(void)
67 {
68 int i;
69
70 if (dma_initialized) {
71 return E_BAD_STATE;
72 }
73
74 /* Initialize any system-level DMA settings */
75 SYS_DMA_Init();
76
77 /* Initialize mutex */
78 mxc_free_lock(&dma_lock);
79 if (mxc_get_lock(&dma_lock, 1) != E_NO_ERROR) {
80 return E_BUSY;
81 }
82
83 /* Ensure all channels are disabled at start, clear flags, init handles */
84 MXC_DMA->cn = 0;
85 for (i = 0; i < MXC_DMA_CHANNELS; i++) {
86 dma_resource[i].valid = 0;
87 dma_resource[i].instance = 0;
88 dma_resource[i].id = i;
89 dma_resource[i].regs = (mxc_dma_ch_regs_t *)&MXC_DMA->ch[i];
90 dma_resource[i].regs->cfg = 0;
91 dma_resource[i].regs->st = dma_resource[i].regs->st;
92
93 dma_resource[i].cb = NULL;
94 }
95 dma_initialized++;
96 mxc_free_lock(&dma_lock);
97
98 return E_NO_ERROR;
99 }
100
101 /* Shut down DMA in an orderly manner, informing clients that their requests did not complete */
DMA_Shutdown(void)102 int DMA_Shutdown(void)
103 {
104 int i;
105
106 if (!dma_initialized) {
107 /* Never initialized, so shutdown is not appropriate */
108 return E_BUSY;
109 }
110
111 if (mxc_get_lock(&dma_lock, 1) != E_NO_ERROR) {
112 return E_BUSY;
113 }
114
115 /* Prevent any new resource allocation by this API */
116 dma_initialized = 0;
117 /* Disable interrupts, preventing future callbacks */
118 MXC_DMA->cn = 0;
119
120 /* For each channel:
121 * - invalidate the handles held by clients
122 * - stop any transfer in progress
123 */
124 for (i = 0; i < MXC_DMA_CHANNELS; i++) {
125 dma_resource[i].regs->cfg = 0;
126 if (dma_resource[i].valid) {
127 dma_resource[i].valid = 0;
128 if (dma_resource[i].cb != NULL) {
129 dma_resource[i].cb(i, E_SHUTDOWN);
130 }
131 }
132 }
133
134 /* Disable any system-level DMA settings */
135 SYS_DMA_Shutdown();
136
137 mxc_free_lock(&dma_lock);
138
139 return E_NO_ERROR;
140 }
141
142 /* Request DMA channel */
143 /* Once "owned", this channel may be used directly via the DMA_GetCHRegs(ch) pointer, or */
144 /* configured via the API functions */
DMA_AcquireChannel(void)145 int DMA_AcquireChannel(void)
146 {
147 int i, channel;
148
149 /* Check for initialization */
150 if (!dma_initialized) {
151 return E_BAD_STATE;
152 }
153
154 /* If DMA is locked return busy */
155 if (mxc_get_lock(&dma_lock, 1) != E_NO_ERROR) {
156 return E_BUSY;
157 }
158
159 /* Default is no channel available */
160 channel = E_NONE_AVAIL;
161 if (dma_initialized) {
162 for (i = 0; i < MXC_DMA_CHANNELS; i++) {
163 if (!dma_resource[i].valid) {
164 /* Found one */
165 channel = i;
166 dma_resource[i].valid = 1;
167 dma_resource[i].regs->cfg = 0;
168 dma_resource[i].regs->cnt_rld = 0; /* Used by DMA_Start() to conditionally set RLDEN */
169 break;
170 }
171 }
172 }
173 mxc_free_lock(&dma_lock);
174
175 return channel;
176 }
177
178 /* Release DMA channel */
179 /* Callbacks will not be called */
DMA_ReleaseChannel(int ch)180 int DMA_ReleaseChannel(int ch)
181 {
182 if (CHECK_HANDLE(ch)) {
183 if (mxc_get_lock(&dma_lock, 1) != E_NO_ERROR) {
184 return E_BUSY;
185 }
186 dma_resource[ch].valid = 0;
187 dma_resource[ch].regs->cfg = 0;
188 dma_resource[ch].regs->st = dma_resource[ch].regs->st;
189 mxc_free_lock(&dma_lock);
190 } else {
191 return E_BAD_PARAM;
192 }
193
194 return E_NO_ERROR;
195 }
196
197 /* Channel configuration */
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)198 int DMA_ConfigChannel(int ch,
199 dma_priority_t prio,
200 dma_reqsel_t reqsel, unsigned int reqwait_en,
201 dma_timeout_t tosel, dma_prescale_t pssel,
202 dma_width_t srcwd, unsigned int srcinc_en,
203 dma_width_t dstwd, unsigned int dstinc_en,
204 unsigned int burst_size, unsigned int chdis_inten,
205 unsigned int ctz_inten)
206 {
207 if (CHECK_HANDLE(ch) && (burst_size > 0)) {
208 /* Designed to be safe, not speedy. Should not be called often */
209 dma_resource[ch].regs->cfg =
210 ((reqwait_en ? MXC_F_DMA_CFG_REQWAIT : 0) |
211 (srcinc_en ? MXC_F_DMA_CFG_SRCINC : 0) |
212 (dstinc_en ? MXC_F_DMA_CFG_DSTINC : 0) |
213 (chdis_inten ? MXC_F_DMA_CFG_CHDIEN : 0) |
214 (ctz_inten ? MXC_F_DMA_CFG_CTZIEN : 0) |
215 prio |reqsel | tosel | pssel |
216 (srcwd << MXC_F_DMA_CFG_SRCWD_POS) |
217 (dstwd << MXC_F_DMA_CFG_DSTWD_POS) |
218 (((burst_size - 1) << MXC_F_DMA_CFG_BRST_POS) & MXC_F_DMA_CFG_BRST));
219 } else {
220 return E_BAD_PARAM;
221 }
222
223 return E_NO_ERROR;
224 }
225
226 /*
227 * DMA request selects for peripherals will override either src_addr or dst_addr.
228 * In these cases, the overridden address is a don't care and may be 0.
229 */
DMA_SetSrcDstCnt(int ch,void * src_addr,void * dst_addr,unsigned int count)230 int DMA_SetSrcDstCnt(int ch,
231 void *src_addr,
232 void *dst_addr,
233 unsigned int count)
234 {
235 if (CHECK_HANDLE(ch)) {
236 dma_resource[ch].regs->src = (unsigned int)src_addr;
237 dma_resource[ch].regs->dst = (unsigned int)dst_addr;
238 dma_resource[ch].regs->cnt = count;
239 } else {
240 return E_BAD_PARAM;
241 }
242
243 return E_NO_ERROR;
244 }
245
246 /* Must set en_reload == 1 to have any effect */
DMA_SetReload(int ch,void * src_addr_reload,void * dst_addr_reload,unsigned int count_reload)247 int DMA_SetReload(int ch,
248 void *src_addr_reload,
249 void *dst_addr_reload,
250 unsigned int count_reload)
251 {
252 if (CHECK_HANDLE(ch)) {
253 dma_resource[ch].regs->src_rld = (unsigned int)src_addr_reload;
254 dma_resource[ch].regs->dst_rld = (unsigned int)dst_addr_reload;
255 if (dma_resource[ch].regs->cfg & MXC_F_DMA_CFG_CHEN) {
256 /* If channel is already running, set RLDEN to enable next reload */
257 dma_resource[ch].regs->cnt_rld = MXC_F_DMA_CNT_RLD_RLDEN | count_reload;
258 } else {
259 /* Otherwise, this is the initial setup, so DMA_Start() will handle setting that bit */
260 dma_resource[ch].regs->cnt_rld = count_reload;
261 }
262 } else {
263 return E_BAD_PARAM;
264 }
265
266 return E_NO_ERROR;
267 }
268
DMA_SetCallback(int ch,void (* callback)(int,int))269 int DMA_SetCallback(int ch, void (*callback)(int, int))
270 {
271 if (CHECK_HANDLE(ch)) {
272 /* Callback for interrupt handler, no checking is done, as NULL is valid for (none) */
273 dma_resource[ch].cb = callback;
274 } else {
275 return E_BAD_PARAM;
276 }
277
278 return E_NO_ERROR;
279 }
280
281 /* Interrupt enable/disable */
DMA_EnableInterrupt(int ch)282 int DMA_EnableInterrupt(int ch)
283 {
284 if (CHECK_HANDLE(ch)) {
285 MXC_DMA->cn |= (1 << ch);
286 } else {
287 return E_BAD_PARAM;
288 }
289
290 return E_NO_ERROR;
291 }
292
DMA_DisableInterrupt(int ch)293 int DMA_DisableInterrupt(int ch)
294 {
295 if (CHECK_HANDLE(ch)) {
296 MXC_DMA->cn &= ~(1 << ch);
297 } else {
298 return E_BAD_PARAM;
299 }
300
301 return E_NO_ERROR;
302 }
303
304 /* Channel interrupt flags */
DMA_GetFlags(int ch,unsigned int * fl)305 int DMA_GetFlags(int ch, unsigned int *fl)
306 {
307 if (CHECK_HANDLE(ch) && fl) {
308 *fl = dma_resource[ch].regs->st;
309 } else {
310 return E_BAD_PARAM;
311 }
312
313 return E_NO_ERROR;
314 }
315
DMA_ClearFlags(int ch)316 int DMA_ClearFlags(int ch)
317 {
318 if (CHECK_HANDLE(ch)) {
319 dma_resource[ch].regs->st = dma_resource[ch].regs->st;
320 } else {
321 return E_BAD_PARAM;
322 }
323
324 return E_NO_ERROR;
325 }
326
327 /* Start channel */
DMA_Start(int ch)328 int DMA_Start(int ch)
329 {
330 if (CHECK_HANDLE(ch)) {
331 DMA_ClearFlags(ch);
332 if (dma_resource[ch].regs->cnt_rld) {
333 dma_resource[ch].regs->cfg |= (MXC_F_DMA_CFG_CHEN | MXC_F_DMA_CFG_RLDEN);
334 } else {
335 dma_resource[ch].regs->cfg |= MXC_F_DMA_CFG_CHEN;
336 }
337 } else {
338 return E_BAD_PARAM;
339 }
340
341 return E_NO_ERROR;
342 }
343
344 /* Stop channel */
DMA_Stop(int ch)345 int DMA_Stop(int ch)
346 {
347 if (CHECK_HANDLE(ch)) {
348 dma_resource[ch].regs->cfg &= ~MXC_F_DMA_CFG_CHEN;
349 } else {
350 return E_BAD_PARAM;
351 }
352
353 return E_NO_ERROR;
354 }
355
356 /* Get pointer to registers, for advanced users */
DMA_GetCHRegs(int ch)357 mxc_dma_ch_regs_t *DMA_GetCHRegs(int ch)
358 {
359 if (CHECK_HANDLE(ch)) {
360 return dma_resource[ch].regs;
361 } else {
362 return NULL;
363 }
364 }
365
366 /* */
DMA_Handler(int ch)367 void DMA_Handler(int ch)
368 {
369 /* Do callback, if enabled */
370 if (dma_resource[ch].cb != NULL) {
371 dma_resource[ch].cb(ch, E_NO_ERROR);
372 }
373 DMA_ClearFlags(ch);
374 }
375