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