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

     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 }