Fri, 13 Aug 2010 10:43:05 +0100
Initial commit, DMAC version 3.1
1 /****************************************************************************
2 **
3 ** Name: MicoDMA.c
4 **
5 ** Description:
6 ** Implements functions for manipulating LatticeMico32 DMA
7 **
8 ** Revision: 3.0
9 **
10 ** Disclaimer:
11 **
12 ** This source code is intended as a design reference which
13 ** illustrates how these types of functions can be implemented. It
14 ** is the user's responsibility to verify their design for
15 ** consistency and functionality through the use of formal
16 ** verification methods. Lattice Semiconductor provides no warranty
17 ** regarding the use or functionality of this code.
18 **
19 ** --------------------------------------------------------------------
20 **
21 ** Lattice Semiconductor Corporation
22 ** 5555 NE Moore Court
23 ** Hillsboro, OR 97214
24 ** U.S.A
25 **
26 ** TEL: 1-800-Lattice (USA and Canada)
27 ** (503)268-8001 (other locations)
28 **
29 ** web: http://www.latticesemi.com
30 ** email: techsupport@latticesemi.com
31 **
32 ** --------------------------------------------------------------------------
33 **
34 ** Change History (Latest changes on top)
35 **
36 ** Ver Date Description
37 ** --------------------------------------------------------------------------
38 **
39 ** 3.0 Mar-25-2008 Added Header
40 **
41 **---------------------------------------------------------------------------
42 *****************************************************************************/
45 #include "MicoDMA.h"
46 #include "MicoDMAService.h"
47 #include "MicoInterrupts.h"
50 #ifdef __cplusplus
51 extern "C" {
52 #endif
54 #define MICODMA_CTX_DMA_PAUSED (0x00000001)
57 /* program a DMA operation based on the descriptor contents */
58 static void MicoDMA_Program(unsigned int DMABase, DMADesc_t *desc)
59 {
60 volatile MicoDMA_t *dma = (volatile MicoDMA_t *)DMABase;
62 /* program the source-address */
63 dma->sAddr = desc->sAddr;
65 /* program the destination-address */
66 dma->dAddr = desc->dAddr;
68 /* program length */
69 dma->len = desc->reserved;
71 /* program type */
72 dma->control = desc->type;
74 /* start DMA */
75 dma->status = (MICODMA_STATUS_IE|MICODMA_STATUS_START);
77 /* all done */
78 return;
79 }
82 /* handles interrupts from the DMA device */
83 static void MicoDMA_ISR(unsigned int isrCtx, void *data)
84 {
85 volatile unsigned int iStatus;
86 DMADesc_t *desc;
87 MicoDMACtx_t *ctx;
88 volatile MicoDMA_t *dma;
90 ctx = (MicoDMACtx_t *)data;
91 dma = (volatile MicoDMA_t *)ctx->base;
92 desc = (DMADesc_t *)ctx->pHead;
95 /* read the status: this acknowledges device's interrupt */
96 iStatus = dma->status;
98 /* if this is a spurious interrupt, ignore it */
99 if(desc == 0){
100 return;
101 }
103 /* set the head-descriptor's status accordingly */
104 desc->state = ((iStatus & MICODMA_STATUS_SUCCESS) == 0)?MICODMA_STATE_SUCCESS:
105 MICODMA_STATE_ERROR;
107 /* fetch the next descriptor */
108 if(desc->next == desc){
109 /* this was the last descriptor in the list */
110 ctx->pHead = 0;
111 }else{
112 /* there are more descriptors queued up */
113 desc->prev->next = desc->next;
114 desc->next->prev = desc->prev;
115 ctx->pHead = (void *)desc->next;
116 }
118 /* invoke the callback routine if there's one specified */
119 if(desc->onCompletion)
120 desc->onCompletion(desc, desc->state);
123 /*
124 * If pause-flag isn't set, go ahead and start
125 * the next dma transaction
126 */
127 if( ((ctx->flags & MICODMA_CTX_DMA_PAUSED)==0) && ctx->pHead)
128 MicoDMA_Program(ctx->base, (DMADesc_t *)ctx->pHead);
131 /* all done */
132 return;
133 }
136 #ifdef __cplusplus
137 }
138 #endif
141 /* initialization routine */
142 void MicoDMAInit(MicoDMACtx_t *ctx)
143 {
144 /*
145 * Unfortunately, we cannot "stop" the DMA if it was already
146 * running..
147 */
148 volatile int i;
149 volatile MicoDMA_t *dma = (volatile MicoDMA_t *)ctx->base;
152 i = dma->status;
153 dma->status = 0;
156 /* set to "no flags" */
157 ctx->flags = 0;
160 /* compute max-length based on the length-register width */
161 if(ctx->maxLength > 0){
162 i = ctx->maxLength;
163 ctx->maxLength = 1;
164 do{
165 ctx->maxLength += ctx->maxLength;
166 }while(--i > 0);
167 ctx->maxLength--;
168 }
171 /* initialize descriptor to null */
172 ctx->pCurr = 0;
173 ctx->pHead = 0;
176 /* register ISR for this DMA */
177 MicoRegisterISR(ctx->irq, (void *)ctx, MicoDMA_ISR);
180 /* Register this DMA as an available device for the lookup service */
181 ctx->lookupReg.name = ctx->name;
182 ctx->lookupReg.deviceType = "DMADevice";
183 ctx->lookupReg.priv = ctx;
184 MicoRegisterDevice( &(ctx->lookupReg) );
187 /* all done */
188 return;
189 }
192 /* queue a new descriptor */
193 unsigned int MicoDMAQueueRequest(MicoDMACtx_t *ctx, DMADesc_t *desc, DMACallback_t callback)
194 {
195 int byteLength;
196 DMADesc_t *pPrevTail, *pHead;
199 /* make sure descriptor and context are valid */
200 if((ctx == 0) || (desc == 0))
201 return(MICODMA_ERR_INVALID_POINTERS);
204 /* convert read/write length into byte-length */
205 byteLength = desc->length;
206 if(desc->type & DMA_16BIT_TRANSFER){
207 byteLength += byteLength; /* left-shift by 1 to multiply by 2 for 16-bit transfer */
208 } else if(desc->type & DMA_32BIT_TRANSFER) {
209 byteLength += byteLength; /* left-shift by 1 to multiply by 2 */
210 byteLength += byteLength; /* left-shift by 1 to multiply by 2, again */
211 }
215 /* make sure length of transaction is okay */
216 if((byteLength > ctx->maxLength) || (byteLength == 0))
217 return(MICODMA_ERR_DESC_LEN_ERR);
220 /* save the new byte-length */
221 desc->reserved = byteLength;
224 /* prepare descriptor */
225 desc->state = MICODMA_STATE_PENDING;
226 desc->onCompletion = callback;
229 /* disable this DMA's interrupt */
230 MicoDisableInterrupt(ctx->irq);
233 /* attach to this DMA's descriptor list */
234 if(ctx->pHead == 0){
235 ctx->pHead = (void *)desc;
236 desc->next = desc;
237 desc->prev = desc;
239 /*
240 * Since this is the first element in the list,
241 * kick-off the DMA, if we're not paused
242 */
243 if((ctx->flags & MICODMA_CTX_DMA_PAUSED)==0)
244 MicoDMA_Program(ctx->base, desc);
246 }else{
247 pHead = (DMADesc_t *)(ctx->pHead);
248 pPrevTail = pHead->prev;
250 pPrevTail->next = desc;
251 pHead->prev = desc;
252 desc->next = pHead;
253 desc->prev = pPrevTail;
254 }
256 /* reenable this DMA's interrupts */
257 MicoEnableInterrupt(ctx->irq);
259 return(0);
260 }
263 /* dequeue an existing descriptor */
264 unsigned int MicoDMADequeueRequest(MicoDMACtx_t *ctx, DMADesc_t *desc, unsigned int callback)
265 {
266 DMADesc_t *pHead, *pTail;
268 /* cannot dequeue a request if it is in a state other than pending */
269 if((ctx == 0) || (desc == 0) || (desc->next == 0) || (desc->prev == 0))
270 return(MICODMA_ERR_INVALID_POINTERS);
273 /* disable interrupts */
274 MicoDisableInterrupt(ctx->irq);
276 if(desc->state != MICODMA_STATE_PENDING){
278 /* cannot dequeue a request that isn't "pending" */
279 MicoEnableInterrupt(ctx->irq);
280 return(MICODMA_ERR_DESCRIPTOR_NOT_PENDING);
282 }else{
283 /*
284 * mark this descriptor as non-pending and
285 * remove it from the queue
286 */
287 desc->state = MICODMA_STATE_ABORTED;
288 if(desc->next != desc){
289 /* this isn't the only descriptor in the list */
290 pHead = (DMADesc_t *)ctx->pHead;
291 pTail = pHead->prev;
293 /* adjust pointers */
294 desc->prev->next = desc->next;
295 desc->next->prev = desc->prev;
297 /* if this was the head, readjust the head */
298 if(pHead == desc)
299 pHead = desc->next;
300 }else{
301 /* this was the only descriptor */
302 ctx->pHead = 0;
303 }
304 desc->prev = 0;
305 desc->next = 0;
307 /* call the callback with 'abort status' if requested */
308 if(callback != 0){
309 (desc->onCompletion)(desc, MICODMA_STATE_ABORTED);
310 }
312 /* reenable interrupt */
313 MicoEnableInterrupt(ctx->irq);
315 /* all done successfully */
316 return(0);
317 }
319 }
322 /* query status of a descriptor */
323 unsigned int MicoDMAGetState(DMADesc_t *desc)
324 {
325 return(desc->state);
326 }
329 /* pause DMA operations (after completion of the currently active descr.) */
330 unsigned int MicoDMAPause(MicoDMACtx_t *ctx)
331 {
332 if(ctx == 0)
333 return(MICODMA_ERR_INVALID_POINTERS);
335 /* disable interrupt for this DMA device */
336 MicoDisableInterrupt(ctx->irq);
338 /* set the pause flag */
339 ctx->flags |= MICODMA_CTX_DMA_PAUSED;
341 /* enable interrupts */
342 MicoEnableInterrupt(ctx->irq);
344 return(0);
345 }
349 /* resume DMA operations */
350 unsigned int MicoDMAResume(MicoDMACtx_t *ctx)
351 {
352 if(ctx == 0)
353 return(MICODMA_ERR_INVALID_POINTERS);
355 /* disable interrupt for the dma-device */
356 MicoDisableInterrupt(ctx->irq);
358 if(ctx->flags & MICODMA_CTX_DMA_PAUSED){
359 /* reset the pause-flag */
360 ctx->flags &= ~(MICODMA_CTX_DMA_PAUSED);
362 /*
363 * If there are descriptors queued up,
364 * kick-start the dma process
365 */
366 if(ctx->pHead)
367 MicoDMA_Program(ctx->base, (DMADesc_t *)ctx->pHead);
369 }
371 /* reenable interrupt for this dma device */
372 MicoEnableInterrupt(ctx->irq);
374 return(0);
375 }