drivers/device/MicoDMA.c

Fri, 13 Aug 2010 10:43:05 +0100

author
Philip Pemberton <philpem@philpem.me.uk>
date
Fri, 13 Aug 2010 10:43:05 +0100
changeset 0
11aef665a5d8
permissions
-rw-r--r--

Initial commit, DMAC version 3.1

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